Por que alloc e init são chamados separadamente em Objective-C?
-
21-09-2019 - |
Pergunta
Observação:Sou relativamente novo em Objective-C e venho de Java e PHP.
Alguém poderia me explicar por que sempre tenho que primeiro alocar e depois inicializar uma instância?
Isso não poderia ser feito nos métodos init como este:
+ (MyClass*)init {
MyClass *instance = [MyClass alloc];
[instance setFoo:@"bla"];
return instance;
}
+ (MyClass*)initWithString:(NSString*)text {
MyClass *instance = [MyClass init];
[instance setFoo:text];
return instance;
}
...
Isso é apenas uma relíquia dos velhos tempos C ou há algo que não estou vendo?
Eu sei que isso não é um problema, pois eu poderia sempre chamar alloc e init, mas como é um pouco tedioso, gostaria de pelo menos saber por que estou fazendo isso.
Estou gostando da expressividade da linguagem até agora, mas isso é algo que quero entender completamente para pensar do jeito Objective-C.
Obrigado!
Solução
+Novo acaba enviando uma mensagem de alocação +para a classe e uma mensagem -init para o que voltar do +aloc.
A razão pela qual a próxima partiu da convenção de Stepstone de usar a nova mensagem (que era uma idéia de pequeno porte) é que, no início, eles encontraram situações em que queriam poder inicializar o mesmo objeto mais de uma vez.
Outras dicas
Porque criar uma instância e inicializar uma instância são dois trabalhos separados.
Você envia um alloc
mensagem para a classe para obter uma instância não inicializada. Você deve inicializar a instância e geralmente tem várias maneiras de fazer isso. Por exemplo:
myStr = [[NSString alloc] init]; //Empty string
myStr = [[NSString alloc] initWithFormat:@"%@.%@", parentKeyPath, key];
myStr = [[NSString alloc] initWithData:utf16data encoding:NSUnicodeStringEncoding error:&error];
myStr = [[NSString alloc] initWithContentsOfURL:URL encoding:NSUTF8StringEncoding error:&error];
Cada um deles inicializa a string de uma maneira completamente diferente. Como você inicializa a string depende do que você deseja inicializá -la.
Claro, ninguém gosta de escrever alloc
e depois init
e depois autorelease
toda vez, então você geralmente tem métodos de conveniência (por exemplo, stringWithFormat:
) que dê as três etapas para você.
Editar: Para saber mais sobre este tópico, incluindo idéias essenciais dos comentaristas, consulte minha postagem no blog “Reunificação”.
Ver NSZone
.
+alloc
é um corte de atalho para +allocWithZone:
, que é um mecanismo que o cacau fornece para otimizar a alocação de memória.
Então você tem a opção de fazer algo assim:
foo = [[NSString allocWithZone:MyZone] initWithString:@"Foo"];
foo2 = [foo copyWithZone:MyZone];
A idéia por trás das zonas de memória é que, se você tiver um grande número de objetos semelhantes que são frequentemente alocados e desalocados, pode ser mais eficiente usar uma zona de memória separada para esses objetos.
Para que o zoneamento seja eficaz, você gostaria de ter +allocWithZone:
Disponível para todas as subclasse NSObject, portanto, você precisa separar a alocação e a inicialização. Você pode criar e usar todos os atalhos que deseja, como +new
, mas por baixo de tudo, você precisa de um -init
Método que inicializa um objeto que já foi alocado.
“Separar os estágios de alocação e inicialização da criação de instâncias oferece muitos benefícios.É possível usar qualquer variação do método de classe +alloc para alocar uma instância e então usar qualquer inicializador disponível com a nova instância. Isso torna possível criar seus próprios métodos de inicialização sem a necessidade de fornecer implementações alternativas de todos os métodos de alocação.Raramente são criados novos métodos de alocação porque os métodos existentes atendem a quase todas as necessidades.Entretanto, um ou mais novos inicializadores são criados para quase todas as classes.Devido à separação dos estágios de alocação e inicialização, as implementações de inicializadores só precisam lidar com as variáveis de novas instâncias e podem ignorar completamente os problemas que envolvem a alocação. A separação simplifica o processo de escrita de inicializadores.Além disso, inicializadores padrão Cocoa como -initWithCoder:trabalhar com instâncias independentemente da forma como a memória da instância foi alocada.Uma consequência negativa da separação entre alocação e inicialização é a necessidade de estar ciente de convenções como o inicializador designado. Você deve saber quais métodos são inicializadores designados e como criar e documentar novos inicializadores em subclasses.No longo prazo, o uso de inicializadores designados simplifica o desenvolvimento de software, mas há um argumento de que o padrão de criação em dois estágios aumenta a curva de aprendizado inicial dos desenvolvedores do Cocoa.
(c) Padrões de Design de Cacau por Erik M.Buck e Donald A.Yacktman
Você não precisa. Você pode usar [MyClass new]
. Isso é semelhante ao seu hipotético init
método.
Basicamente, o Objective-C, que não teve coleta de lixo inicialmente, separa o conceito de alocação de memória e inicialização da classe. É por isso que existem dois métodos distintos. Quando Você ligar alloc
, você está alocando explicitamente a memória.
A maioria das aulas tem o que você está pedindo. Você já recebeu respostas antes de por que é e por que nem sempre gostaria de usar isso o tempo todo, mas se você ler a documentação para as aulas, verá muitos métodos de classe que agem dessa maneira e eles são frequentemente usados.
Para NSString, você tem, por exemplo:
+ (id)string // (Empty string)
+ (id)stringWithFormat:... // Formatted string (like you use)
+ (id)stringWithContentsOfURL:... // String populated with contents of URL
E assim por diante. E você então usaria isso como: NSString *myString = [NSString stringWithFormat:@"Hello %@\n", userName];
A maioria das outras classes tem isso, como nsarray:
+ (id)array
+ (id)arrayWithContentsOfFile:...
+ (id)arrayWithContentsOfURL:...
+ (id)arrayWithObjects:...
Você só precisa ler a documentação. :) E leia as outras respostas sobre por que você não quer usar muito isso.
aloc : A memória é alocada/dada à referência de objetos. Agora, a referência tem a posse da memória, mas ainda não fez nada. Essa memória está vazia (caso mais raro) ou com alguns dados anônimos.
aloc e init : A memória alocada é limpa/esvaziada. A memória é iniciada por bit zero.
aloc e initwithdata ... : A memória alocada é iniciada com dados desejados respeitados às propriedades da classe.
Por exemplo, quando você compra um enredo, obtém a posse. Este enredo é dado a você como é, tijolos em ruínas ou casa velha podem estar lá. Isto é aloc.
Ao limpar o enredo e remover toda a sujeira e ninhada. Isto é aloc com init.
Quando você construa isso em uma casa valiosa, torna -se mais significativo para você. E isso é Aloc initwith ...