J'ai une question générale sur l'écriture de méthodes init en Objective-C.
Je vois partout (code Apple, livres, code open source, etc.) qu'une méthode init doit vérifier si self = [super init] n'est pas nul avant de continuer l'initialisation.
Le modèle Apple par défaut pour une méthode init est:
- (id) init
{
self = [super init];
if (self != nil)
{
// your code here
}
return self;
}
Pourquoi?
Je veux dire quand init retournera-t-il jamais nul? Si j'appelle init sur NSObject et que j'obtiens nul, alors quelque chose doit être vraiment foutu, non? Et dans ce cas, autant ne pas écrire de programme ...
Est-il vraiment si courant qu'une méthode init de classe puisse retourner nil? Si oui, dans quel cas et pourquoi?
objective-c
null
init
Jasarien
la source
la source
Réponses:
Par exemple:
... etc. (Remarque: NSData peut lever une exception si le fichier n'existe pas). Il existe de nombreux domaines dans lesquels le retour de zéro est le comportement attendu lorsqu'un problème survient, et pour cette raison, il est de pratique courante de vérifier pratiquement tout le temps la valeur de zéro, par souci de cohérence.
la source
Cet idiome particulier est standard car il fonctionne dans tous les cas.
Bien que rare, il y aura des cas où ...
... renvoie une instance différente, nécessitant ainsi l'assignation à soi.
Et il y aura des cas où il renverra nil, nécessitant ainsi la vérification de nil pour que votre code n'essaye pas d'initialiser un emplacement de variable d'instance qui n'existe plus.
L'essentiel est que c'est le modèle correct documenté à utiliser et, si vous ne l'utilisez pas, vous le faites mal.
la source
-init
afaict…)[super init]
renvoie nul lorsque la superclasse directe estNSObject
? N'est-ce pas un cas où «tout est cassé»?NSObject
c'est la superclasse directe. Mais ... même si vous déclarezNSObject
comme superclasse directe, quelque chose aurait pu être modifié à l 'exécution de sorte que lNSObject
' implémentation deinit
ne soit pas ce que l 'on appelle en réalité.Je pense que, dans la plupart des classes, si la valeur de retour de [super init] est nulle et que vous la vérifiez, comme recommandé par les pratiques standard, puis que vous la renvoyez prématurément si elle est nulle, en gros, votre application ne fonctionnera toujours pas correctement. Si vous pensez à ce sujet, même si que si (auto! = Nil) chèque est là, pour le bon fonctionnement de votre classe, 99,99% du temps que vous avez réellement faire besoin d'être auto non nul. Maintenant, supposons que, pour une raison quelconque, [super init] ait renvoyé nil, fondamentalement votre vérification contre nil consiste essentiellement à passer la balle à l'appelant de votre classe, où il échouerait probablement de toute façon, car il supposera naturellement que l'appel était réussi.
En gros, ce à quoi je veux en venir, c'est que 99,99% du temps, le if (self! = Nil) ne vous achète rien en termes de plus grande robustesse, puisque vous ne faites que passer la responsabilité à votre invocateur. Pour vraiment être en mesure de gérer cela de manière robuste, vous devez en fait mettre des vérifications dans toute votre hiérarchie d'appel. Et même dans ce cas, la seule chose que cela vous achèterait, c'est que votre application échouerait un peu plus proprement / solidement. Mais cela échouerait toujours.
Si une classe de bibliothèque décide arbitrairement de retourner nil à la suite d'un [super init], vous êtes de toute façon f *** ed, et c'est plus une indication que l'auteur de la classe de bibliothèque a fait une erreur d'implémentation.
Je pense que c'est plus une suggestion de codage héritée, lorsque les applications s'exécutaient dans une mémoire beaucoup plus limitée.
Mais pour le code de niveau C, je vérifierais toujours généralement la valeur de retour de malloc () par rapport à un pointeur NULL. Alors que, pour Objective-C, jusqu'à ce que je trouve des preuves du contraire, je pense que je vais généralement sauter les vérifications if (self! = Nil). Pourquoi cet écart?
Car, aux niveaux C et malloc, dans certains cas, vous pouvez en fait récupérer partiellement. Alors que je pense qu'en Objective-C, dans 99,99% des cas, si [super init] retourne nil, vous êtes fondamentalement f *** ed, même si vous essayez de le gérer. Vous pouvez tout aussi bien laisser l'application planter et gérer les conséquences.
la source
C'est une sorte de résumé des commentaires ci-dessus.
Disons que la superclasse revient
nil
. Qu'est-ce qui va arriver?Si vous ne suivez pas les conventions
Votre code va planter au milieu de votre
init
méthode. (sauf siinit
rien n'a d'importance)Si vous suivez les conventions, ne sachant pas que la superclasse peut retourner nil (la plupart des gens se retrouvent ici)
Votre code va probablement planter plus tard, car votre instance se trouve
nil
là où vous vous attendiez à quelque chose de différent. Ou votre programme va se comporter de manière inattendue sans planter. Oh cher! Voulez-vous cela? Je ne sais pas...Si vous suivez les conventions, autorisez volontairement votre sous-classe à renvoyer nil
Votre documentation de code (!) Doit clairement indiquer: "retourne ... ou nil", et le reste de votre code doit être préparé pour gérer cela. Maintenant, cela a du sens.
la source
En règle générale, si votre classe dérive directement de
NSObject
, vous n'en aurez pas besoin. Cependant, c'est une bonne habitude à prendre, car si votre classe dérive d'autres classes, leurs initialiseurs peuvent revenirnil
, et si c'est le cas, votre initialiseur peut alors capturer cela et se comporter correctement.Et oui, pour mémoire, je suis les meilleures pratiques et je l'écris sur toutes mes classes, même celles qui en découlent directement
NSObject
.la source
Foo *bar = [[Foo alloc] init]; if (bar) {[bar doStuff];}
NSObject
ne garantit pas que cela-init
vous donneNSObject
aussi, si vous comptez des runtimes exotiques comme les anciennes versions de GNUstep dans (qui retourneGSObject
) donc quoi qu'il arrive, une vérification et une assignation.Vous avez raison, vous pouvez souvent simplement écrire
[super init]
, mais cela ne fonctionnerait pas pour une sous-classe de n'importe quoi. Les gens préfèrent simplement mémoriser une ligne de code standard et l'utiliser tout le temps, même lorsque cela n'est que parfois nécessaire, et ainsi nous obtenons le standardif (self = [super init])
, ce qui prend à la fois la possibilité de renvoyer nul et la possibilité d'un objet autre que celui d'self
être retourné en compte.la source
Une erreur courante est d'écrire
qui renvoie une instance de la superclasse, ce qui n'est PAS ce que vous voulez dans un constructeur de sous-classe / init. Vous récupérez un objet qui ne répond pas aux méthodes de la sous-classe, ce qui peut prêter à confusion et générer des erreurs déroutantes sur le fait de ne pas repondre à des méthodes ou des identifiants non trouvés, etc.
est nécessaire si la super classe a des membres (variables ou d' autres objets) pour initialiser la première avant la mise en place des membres des sous - classes. Sinon, le runtime objc les initialise tous à 0 ou à nil . ( contrairement à ANSI C, qui alloue souvent des morceaux de mémoire sans les effacer du tout )
Et oui, l'initialisation de la classe de base peut échouer en raison d'erreurs de mémoire insuffisante, de composants manquants, d'échecs d'acquisition de ressources, etc. donc une vérification de zéro est judicieuse et prend moins de quelques millisecondes.
la source
C'est pour vérifier que l'intialazation a fonctionné, l'instruction if renvoie true si la méthode init n'a pas retourné nil, c'est donc un moyen de vérifier que la création de l'objet a fonctionné correctement. Peu de raisons pour lesquelles je peux penser que init pourrait échouer peut-être que c'est une méthode d'initialisation surchargée que la super classe ne connaît pas ou quelque chose du genre, je ne pense pas que ce soit si courant. Mais si cela arrive, il vaut mieux que rien n'arrive qu'un crash que je suppose donc c'est toujours vérifié ...
la source
Sous OS X, il est peu probable que l'
-[NSObject init]
échec soit dû à des raisons de mémoire. On ne peut pas en dire autant pour iOS.De plus, c'est une bonne pratique pour écrire lors du sous-classement d'une classe qui pourrait revenir
nil
pour une raison quelconque.la source
-[NSObject init]
est très peu probable qu'il échoue pour des raisons de mémoire car il n'alloue aucune mémoire.