Utilisation de alloc init au lieu de new

344

En apprenant Objective-C et en lisant un exemple de code, je remarque que les objets sont généralement créés à l'aide de cette méthode:

SomeObject *myObject = [[SomeObject alloc] init];

au lieu de:

SomeObject *myObject = [SomeObject new];

Y a-t-il une raison à cela, car j'ai lu qu'ils sont équivalents?

willc2
la source
12
Je ne savais même pas que c'était possible! Tout le matériel que j'ai lu prétend que le nouveau mot-clé n'existe pas!
Jonathan
78
En fait, "nouveau" n'est pas un mot clé dans Objective-C, mais NSObject implémente une méthode de classe "nouveau" qui appelle simplement "alloc" et "init".
Don McCaughey
3
Jonathan, c'est exactement ce qui a poussé ma question. Ils peuvent être fonctionnellement équivalents mais [[alloc] init] est clairement l'idiome dominant.
willc2
1
Je pense qu'Apple (d'après ce dont je me souviens d'une de leurs conférences iTunes Stanford) vous encourage simplement à utiliser alloc init à la place afin que vous puissiez comprendre le processus de ce qui se passe. Ils n'utilisent pas non plus beaucoup de nouveautés dans leur exemple de code, donc alloc init semble être juste une bonne habitude générale qu'Apple essaie de promouvoir.
PostCodeism

Réponses:

290

Il y a un tas de raisons ici: http://macresearch.org/difference-between-alloc-init-and-new

Certains sélectionnés sont:

  • newne prend pas en charge les initialiseurs personnalisés (comme initWithString)
  • alloc-init est plus explicite que new

L'opinion générale semble être que vous devez utiliser tout ce qui vous convient.

Jeremy Stanley
la source
8
Si votre classe utilise -init comme initialiseur désigné, + new l'appellera. Ce à quoi Jeremy fait référence, c'est si vous avez un initialiseur personnalisé qui n'est pas -init. -initWithName:, par exemple.
bbum
87
Rien ne vous empêche également de l'implémenter +newWithString:, si vous l'avez déjà implémenté -initWithString. Pas si commun cependant. Personnellement, je l'utilise toujours newlorsque l'initialiseur désigné est inittrop court et doux.
PeyloW
11
Je sais que c'est un vieux fil, mais je veux juste souligner que vous ne devriez pas l' implémenter +newWithString:. Cela rompt la séparation des préoccupations. Pour quand vous voulez juste utiliser de -inittoute façon, il n'y a aucune raison de ne pas simplement utiliser +new, cependant.
Jonathan Sterling,
7
@JonathanSterling: Apple a de nombreux cas où ils semblent faire exactement ce que vous déconseillez; par exemple [[NSString alloc] initWithFormat:...]et [NSString stringWithFormat:...]sont tous deux équivalents. Voulez-vous dire qu'Apple a violé la séparation des préoccupations et n'aurait pas dû l'implémenter de cette façon? (Remarque: je n'essaie pas d'être condescendant; je voudrais simplement obtenir plus d'informations et savoir si suivre l'exemple d'Apple est parfois une mauvaise idée.)
Senseful
6
@Senseful, je crois que cela remonte à la veille de l'ARC (ou de la collecte des ordures). Ces deux choses ne sont pas équivalentes. stringWithFormat: retournerait une chaîne libérée automatiquement tandis que alloc: init: devrait être libéré ou libéré automatiquement, sinon cela provoquerait une fuite de mémoire.
msfeldstein
139

Très vieille question, mais j'ai écrit un exemple juste pour le plaisir - peut-être le trouverez-vous utile;)

#import "InitAllocNewTest.h"

@implementation InitAllocNewTest

+(id)alloc{
    NSLog(@"Allocating...");
    return [super alloc];
}

-(id)init{
    NSLog(@"Initializing...");
    return [super init];
}

@end

Dans la fonction principale, les deux instructions:

[[InitAllocNewTest alloc] init];

et

[InitAllocNewTest new];

résultat dans la même sortie:

2013-03-06 16:45:44.125 XMLTest[18370:207] Allocating...
2013-03-06 16:45:44.128 XMLTest[18370:207] Initializing...
guitar_freak
la source
5
Excellente réponse! Je veux juste ajouter que l'utilisation de + new est idéale pour les cas où vous n'avez qu'à utiliser -init et rien de plus spécialisé comme -initWithSomething: ...
cgossain
Malheureusement, cet exemple ne prouve pas qu'ils sont identiques. Ce n'est qu'un exemple où ils produisent le même résultat.
Vince O'Sullivan
10
Malheureusement, cet exemple ne prouve pas qu'ils sont identiques. Ce n'est qu'un exemple où elles se produisent et produisent le même résultat. D'un autre côté, cela prouve qu'ils sont différents ... En prenant l'exemple ci-dessus, modifiez "InitAllocNewTest.h" comme suit: @interface InitAllocNewTest: NSObject - (instancetype) __unavailable init; @end [[InitAllocNewTest alloc] init]ne compilera pas tant qu'il [InitAllocNewTest new]n'est pas affecté. (Excuses pour le manque de sauts de ligne, etc.)
Vince O'Sullivan
1
@ VinceO'Sullivan, bon point. Cela signifie que si l'on veut rendre init indisponible, comme dans une classe singleton, il faut également désactiver new. Je n'aborderai pas ici si les singletons sont bons ou mauvais.
user3099609
J'ai une question du code ci-dessus. pourquoi "retourner [super init];"? généralement init est une méthode où les membres de la classe sont initialisés, pourquoi le changement de pointeur est très bizarre!
Pruthvidhar Rao Nadunooru
52

+newest équivalent à +alloc/-initl' NSObjectimplémentation d'Apple . Il est très peu probable que cela change un jour, mais selon votre niveau de paranoïa, la documentation d'Apple +newsemble permettre un changement d'implémentation (et rompre l'équivalence) à l'avenir. Pour cette raison, parce que "explicite vaut mieux qu'implicite" et pour la continuité historique, la communauté Objective-C évite généralement +new. Cependant, vous pouvez généralement repérer les récents arrivants Java à Objective-C par leur utilisation acharnée de +new.

Barry Wark
la source
8
Bien que cela soit souvent vrai, il existe également un camp en faveur de la concision, auquel cas il n'est plus explicite que lorsqu'il existe une préoccupation claire et présente qui le justifie.
Peter DeWeese
27
J'ai rétrogradé cela car +newil existe depuis les jours suivants. Si quelque +newchose est quelque chose d'un signe de quelqu'un qui a appris l'objc il y a longtemps; Je vois que beaucoup de gens qui reviennent à la langue, ou qui l'ont même écrite pendant des années mais clairement après le boom iOS, n'ont aucune idée de ce que cela +newsignifie. Deuxièmement, comme +newc'est très ancien et depuis les jours suivants, Apple serait assez fou de le changer d'une manière qui casse l'ancien code, d'autant plus que leurs propres bases de code en sont probablement jonchées.
asveikau
1
Je suis presque sûr que l' newidiome vient de Smalltalk. Il est également utilisé dans Ruby, et Objective-C et Ruby tirent une grande partie de leur syntaxe et de leurs conventions de Smalltalk.
Brendon Roberto
3
Il permet uniquement un changement d'allocation. Les documents indiquent explicitement que -init sera appelé. Donc, cela dépend davantage si vous remplacez jamais alloc.
Kendall Helmstetter Gelner le
7
Je ne peux pas vraiment imaginer pourquoi une solution où vous devez taper PLUS serait préférée (alloc init), en particulier dans un langage aussi verbeux comme Objective-C où l'enregistrement des frappes vous fera gagner du temps. Ces «développeurs Java tenaces» utilisent simplement leurs compétences analytiques pour passer moins de temps à écrire du code au lieu de taper inutilement des caractères supplémentaires qui produisent le même résultat fonctionnel.
DiscDev du
9

Souvent, vous devrez passer des arguments à initet vous utiliserez donc une méthode différente, telle que [[SomeObject alloc] initWithString: @"Foo"]. Si vous avez l'habitude d'écrire ceci, vous avez l'habitude de le faire de cette façon et cela [[SomeObject alloc] init]peut donc venir plus naturellement [SomeObject new].

Brian Campbell
la source
7

Une réponse courte est:

  1. Les deux sont les même. Mais
  2. 'new' ne fonctionne qu'avec l'initialiseur de base 'init', et ne fonctionnera pas avec d'autres initialiseurs (par exemple initWithString :).
user739711
la source
3

Pour une note latérale, j'utilise personnellement [Foo new]si je veux que quelque chose en init soit fait sans utiliser sa valeur de retour n'importe où. Si vous n'utilisez pas le retour de [[Foo alloc] init]n'importe où, vous obtiendrez un avertissement. Plus ou moins, j'utilise [Foo new]des bonbons pour les yeux.

evdude100
la source
3

Je suis très en retard à cela, mais je tiens à mentionner que cette nouvelle est en fait dangereuse dans le monde Obj-C avec Swift. Swift ne créera une méthode d'initialisation par défaut que si vous ne créez aucun autre initialiseur. L'appel de new sur une classe swift avec un initialiseur personnalisé provoquera un plantage. Si vous utilisez alloc / init, le compilateur se plaindra correctement que init n'existe pas.

MurderDev
la source
1

Si le nouveau fait le travail pour vous, il réduira également légèrement votre code. Si vous appelez autrement [[SomeClass alloc] init]à de nombreux endroits différents dans votre code, vous créerez un Hot Spot dans l'implémentation de new - c'est-à-dire dans le runtime objc - qui réduira le nombre de vos ratés de cache.

À ma connaissance, si vous devez utiliser un initialiseur personnalisé, utilisez [[SomeClass alloc] initCustom].

Si vous ne le faites pas, utilisez [SomeClass new].

Mike Crawford
la source
1
Je dirais avec "si vous devez utiliser un initialiseur personnalisé, utilisez [[SomeClass alloc] initCustom]". Si vous avez besoin d'un initialiseur personnalisé sans aucun paramètre, ne faites jamais quelque chose comme vous le suggérez, remplacez simplement la initfonction par défaut et utilisez-la. [[SomeClass alloc] init];Si vous avez besoin de paramètres, ne faites toujours jamais quelque chose comme ça, faites-le [[SomeClass alloc] initWith:...];. Enfin, si vous remplacez la initfonction par une implémentation personnalisée, vous pouvez faire appel newà la création d'un objet, et il appellera toujours l' initimplémentation personnalisée .
dirtydanee