Eu tenho uma pergunta geral sobre como escrever métodos init no Objective-C.
Eu vejo em todos os lugares (código da Apple, livros, código-fonte aberto etc.) que um método init deve verificar se self = [super init] não é nulo antes de continuar com a inicialização.
O modelo padrão da Apple para um método init é:
- (id) init
{
self = [super init];
if (self != nil)
{
// your code here
}
return self;
}
Por quê?
Quero dizer, quando o init nunca retornará nada? Se eu chamei init no NSObject e não obtive nada, então algo deve estar realmente ferrado, certo? E, nesse caso, você também pode nem escrever um programa ...
É realmente tão comum que o método init de uma classe possa retornar nulo? Se sim, em que caso e por quê?
objective-c
null
init
Jasarien
fonte
fonte
Respostas:
Por exemplo:
... e assim por diante. (Nota: o NSData pode gerar uma exceção se o arquivo não existir). Existem algumas áreas em que retornar nulo é o comportamento esperado quando um problema ocorre e, por causa disso, é prática comum verificar nulo praticamente o tempo todo, por uma questão de consistência.
fonte
Esse idioma específico é padrão porque funciona em todos os casos.
Embora incomum, haverá casos em que ...
... retorna uma instância diferente, exigindo, assim, a atribuição a si próprio.
E haverá casos em que ele retornará nulo, exigindo a verificação nil, para que seu código não tente inicializar um slot de variável de instância que não exista mais.
A linha inferior é que é o padrão correto documentado para usar e, se você não o estiver usando, estará fazendo errado.
fonte
-init
afeto…) #[super init]
retorna nulo quando a superclasse direta éNSObject
? Não é este o caso em que "tudo está quebrado?"NSObject
é a superclasse direta. Mas ... mesmo se você declararNSObject
como superclasse direta, algo poderia ter sido modificado em tempo de execução, de modo queNSObject
a implementação deinit
não seja o que realmente é chamado.Acho que, na maioria das classes, se o valor de retorno de [super init] for nulo e você o verificar, conforme recomendado pelas práticas padrão, e retornar prematuramente se for nulo, basicamente seu aplicativo ainda não funcionará corretamente. Se você pensar sobre isso, apesar de que, se (auto! = Nil) verificação está lá, para o funcionamento adequado de sua classe, 99,99% do tempo que você realmente fazer necessidade de auto para ser não-nulo. Agora, suponha que, por qualquer motivo, [super init] tenha retornado nulo, basicamente seu cheque contra nulo está basicamente passando a quantia para o chamador de sua classe, onde provavelmente falharia de qualquer maneira, já que naturalmente assumirá que a chamada foi bem sucedido.
Basicamente, o que eu entendo é que 99,99% do tempo, o if (self! = Nil) não compra nada para você em termos de maior robustez, já que você está apenas passando o dinheiro para o invocador. Para realmente ser capaz de lidar com isso de maneira robusta, você realmente precisa verificar a hierarquia de chamadas inteira. E mesmo assim, a única coisa que você compraria é que seu aplicativo falharia um pouco mais limpa / robusta. Mas ainda falharia.
Se uma classe de biblioteca decidiu arbitrariamente retornar nulo como resultado de um [superinicial], você está praticamente fodido de qualquer maneira, e isso é mais uma indicação de que o escritor da classe de biblioteca cometeu um erro de implementação.
Acho que isso é mais uma sugestão de codificação herdada, quando os aplicativos são executados em muito mais memória limitada.
Mas para o código de nível C, eu ainda normalmente verificaria o valor de retorno de malloc () em relação a um ponteiro NULL. Considerando que, para o Objective-C, até encontrar evidências em contrário, acho que geralmente ignorarei as verificações if (self! = Nil). Por que a discrepância?
Porque, nos níveis C e malloc, em alguns casos você pode recuperar parcialmente. Enquanto eu penso no Objective-C, em 99,99% dos casos, se o [super init] retornar nulo, você está basicamente fodido, mesmo que tente lidar com isso. Você também pode deixar o aplicativo falhar e lidar com as consequências.
fonte
Este é um resumo dos comentários acima.
Digamos que a superclasse retorne
nil
. O que vai acontecer?Se você não seguir as convenções
Seu código falhará no meio do seu
init
método. (a menosinit
que nada tenha significado)Se você seguir as convenções, sem saber que a superclasse pode retornar nula (a maioria das pessoas acaba aqui)
Seu código provavelmente travará em algum momento depois, porque sua instância é
nil
onde você esperava algo diferente. Ou seu programa se comportará inesperadamente sem travar. Oh céus! Queres isto? Eu não sei...Se você seguir as convenções, permitindo que sua subclasse retorne nulo
Sua documentação de código (!) Deve indicar claramente: "retorna ... ou nulo", e o restante do seu código precisa estar preparado para lidar com isso. Agora faz sentido.
fonte
Normalmente, se sua classe deriva diretamente
NSObject
, você não precisará. No entanto, é um bom hábito entrar, como se sua classe deriva de outras classes, os inicializadores podem retornarnil
e, nesse caso, o inicializador pode capturar isso e se comportar corretamente.E sim, para constar, eu sigo as melhores práticas e as escrevo em todas as minhas aulas, mesmo nas que derivam diretamente
NSObject
.fonte
Foo *bar = [[Foo alloc] init]; if (bar) {[bar doStuff];}
NSObject
não garante o que-init
você ofereceNSObject
também, se contar tempos de execução exóticos como versões mais antigas do GNUstep em (que retornaGSObject
), então não importa o que seja, uma verificação e uma atribuição.Você está certo, muitas vezes poderia escrever
[super init]
, mas isso não funcionaria para uma subclasse de qualquer coisa. As pessoas preferem apenas memorizar uma linha de código padrão e usá-la o tempo todo, mesmo quando às vezes é necessário, e assim obtemos o padrãoif (self = [super init])
, que leva tanto a possibilidade de retorno nulo quanto a possibilidade de um objeto que nãoself
seja retornado. em conta.fonte
Um erro comum é escrever
que retorna uma instância da superclasse, que NÃO é o que você deseja em um construtor de subclasse / init. Você recebe de volta um objeto que não responde aos métodos da subclasse, que pode ser confuso, e gera erros confusos sobre não responder a métodos ou identificadores não encontrados, etc.
é necessário se a superclasse tiver membros (variáveis ou outros objetos) para inicializar primeiro antes de configurar os membros das subclasses. Caso contrário, o tempo de execução objc inicializa todos eles para 0 ou para zero . ( ao contrário do ANSI C, que geralmente aloca pedaços de memória sem limpá-los )
E sim, a inicialização da classe base pode falhar devido a erros de falta de memória, componentes ausentes, falhas na aquisição de recursos, etc., portanto, uma verificação de zero é sensata e leva menos de alguns milissegundos.
fonte
Isso é para verificar se a intialização funcionou, a instrução if retorna true se o método init não retornar nulo, portanto, é uma maneira de verificar a criação do objeto que funcionou corretamente. Poucas razões pelas quais consigo pensar que o init pode falhar, talvez seja um método de inicialização anulado que a superclasse não conhece ou algo do tipo, eu não acho que isso seja tão comum. Mas se isso acontecer, não é melhor que aconteça uma falha que suponha, para que seja sempre verificada ...
fonte
No OS X, não é provável que haja
-[NSObject init]
falha devido a motivos de memória. O mesmo não pode ser dito para o iOS.Além disso, é uma boa prática escrever quando subclassificar uma classe que possa retornar
nil
por qualquer motivo.fonte
-[NSObject init]
é muito improvável que falhe devido a motivos de memória, pois não aloca nenhuma memória.