Использование alloc init вместо new

344

Изучая Objective-C и читая пример кода, я замечаю, что объекты обычно создаются с помощью этого метода:

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

вместо того:

SomeObject *myObject = [SomeObject new];

Есть ли причина для этого, поскольку я прочитал, что они эквивалентны?

willc2
источник
12
Я даже не знал, что это возможно! Весь материал, который я прочитал, притворяется, что новое ключевое слово не существует!
Джонатан
78
На самом деле «new» не является ключевым словом в Objective-C, но NSObject реализует метод класса «new», который просто вызывает «alloc» и «init».
Дон МакКоги
3
Джонатан, это именно то, что вызвало мой вопрос. Они могут быть функционально эквивалентными, но [[alloc] init] явно является доминирующей идиомой.
willc2
1
Я думаю, что Apple (из того, что я помню из одной из своих лекций по iTunes в Стэнфорде) просто рекомендует вам использовать alloc init вместо этого, чтобы вы могли понять процесс происходящего. Они также не часто используют new в своем примере кода, поэтому alloc init - это, как правило, хорошая привычка, которую Apple пытается продвигать.
PostCodeism

Ответы:

290

Здесь есть несколько причин: http://macresearch.org/difference-between-alloc-init-and-new

Некоторые из них:

  • newне поддерживает пользовательские инициализаторы (как initWithString)
  • alloc-init более явно, чем new

Похоже, что общее мнение заключается в том, что вы должны использовать все, что вам удобно.

Джереми Стэнли
источник
8
Если ваш класс использует -init в качестве назначенного инициализатора, + new вызовет его. Джереми имеет в виду, что у вас есть собственный инициализатор, который не -init. -initWithName :, например.
bbum
87
Ничто не мешает вам также реализовать +newWithString:, если вы уже реализовали -initWithString. Не так часто, хотя. Лично я всегда использую, newкогда назначенный инициализатор init, просто очень короткий и сладкий.
PeyloW
11
Я знаю, что это старая тема, но я просто хочу подчеркнуть, что вы не должны реализовывать +newWithString:. Это нарушает разделение интересов. Хотя, когда вы просто хотите использовать в -initлюбом случае, нет никаких причин, чтобы просто не использовать +new.
Джонатан Стерлинг
7
@JonathanSterling: у Apple есть много случаев, когда они, кажется, делают именно то, против чего вы советуетесь; например [[NSString alloc] initWithFormat:...]и [NSString stringWithFormat:...]оба эквивалентны. Вы говорите, что Apple нарушила разделение интересов и не должна была реализовывать это таким образом? (Примечание: я не пытаюсь быть снисходительным; я просто хотел бы получить больше информации и знать, иногда ли плохой идеей будет следовать примеру Apple.)
Чувственный
6
@ Чувствительный Я считаю, что это относится ко времени до ARC (или сбора мусора). Эти две вещи мы не эквивалентны. stringWithFormat: вернет автоматически освобожденную строку, а alloc: init: потребуется вручную или автоматически освободить, иначе это приведет к утечке памяти.
msfeldstein
139

Очень старый вопрос, но я написал пример просто для удовольствия - может быть, вы найдете его полезным;)

#import "InitAllocNewTest.h"

@implementation InitAllocNewTest

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

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

@end

В основной функции оба утверждения:

[[InitAllocNewTest alloc] init];

и

[InitAllocNewTest new];

результат в том же выводе:

2013-03-06 16:45:44.125 XMLTest[18370:207] Allocating...
2013-03-06 16:45:44.128 XMLTest[18370:207] Initializing...
guitar_freak
источник
5
Отличный ответ! Просто хочу добавить, что использование + new отлично подходит для случаев, когда вам нужно только использовать -init и ничего более специализированного, например -initWithSomething: ...
cgossain
К сожалению, этот пример не является доказательством того, что они идентичны. Это просто пример, где они дают одинаковый результат.
Винс О'Салливан
10
К сожалению, этот пример не является доказательством того, что они идентичны. Это просто пример, когда они происходят, дают одинаковый результат. С другой стороны, это доказывает, что они разные ... Используя приведенный выше пример, измените "InitAllocNewTest.h" следующим образом: @interface InitAllocNewTest: NSObject - (instancetype) __unavailable init; @end [[InitAllocNewTest alloc] init]не будет компилироваться, пока на [InitAllocNewTest new]него не влияют. (Извинения за отсутствие разрывов строк и т. Д.)
Винс О'Салливан
1
@ VinceO'Салливан, хорошая мысль. Это означает, что если кто-то хочет сделать init недоступным, как в одноэлементном классе, нужно также отключить новый. Я не буду здесь касаться хороших или плохих синглетонов.
user3099609
У меня есть вопрос из приведенного выше кода. почему "вернуть [супер инициал]"? Вообще, init - это метод, в котором инициализируются члены класса, поэтому изменение указателя очень странно!
Прутвидхар Рао Надунору
52

+newэквивалентно +alloc/-initв NSObjectреализации Apple . Маловероятно, что это когда-либо изменится, но в зависимости от вашего уровня паранойи документация Apple, по- +newвидимому, позволит изменить реализацию (и нарушить эквивалентность) в будущем. По этой причине, поскольку «явное лучше, чем неявное» и для исторической преемственности сообщество Objective-C обычно избегает +new. Вы можете, однако, как правило , определить последние Java желающих на Objective-C их упорного использования +new.

Барри Уарк
источник
8
Хотя это часто верно, существует также лагерь в пользу краткости, и в этом случае она становится более явной только тогда, когда есть явная и существующая озабоченность, которая этого требует.
Питер ДеВиз
27
Я понизил это, потому +newчто был вокруг с NeXT дней. Если что- +newто является признаком кого-то, кто изучил предмет давно; Я вижу, что многие люди, которые пришли к новому языку или даже писали его годами, но явно после бума iOS, понятия не имеют, что это +newзначит. Во-вторых, поскольку +newон очень старый и со времен NeXT, Apple была бы довольно безумной, чтобы изменить его таким образом, чтобы он ломал старый код, особенно учитывая, что его собственные кодовые базы, вероятно, засорены им.
asveikau
1
Я почти уверен, что newидиома происходит от Smalltalk. Он также используется в Ruby, и Objective-C и Ruby в значительной степени основаны на синтаксисе и соглашениях Smalltalk.
Брендон Роберто
3
Это допускает только изменение в alloc. В документах явно указывается -init. Так что это зависит от того, переопределите ли вы когда-либо.
Кендалл Хельмштеттер Гелнер
7
Я не могу себе представить, почему было бы предпочтительнее решение, в котором вы должны вводить MORE (alloc init), особенно в таком многословном языке, как Objective-C, где сохранение нажатий клавиш сэкономит ваше время. Эти «упрямые Java-разработчики» просто используют свои аналитические способности, чтобы тратить меньше времени на написание кода, вместо того, чтобы излишне набирать дополнительные символы, которые дают тот же функциональный результат.
DiscDev
9

Часто вам нужно будет передавать аргументы, initи поэтому вы будете использовать другой метод, например [[SomeObject alloc] initWithString: @"Foo"]. Если вы привыкли писать это, у вас есть привычка делать это таким образом, и это [[SomeObject alloc] init]может произойти более естественно [SomeObject new].

Брайан Кэмпбелл
источник
7

Один короткий ответ:

  1. Оба одинаковы. Но
  2. 'new' работает только с базовым инициализатором 'init' и не будет работать с другими инициализаторами (например, initWithString :).
user739711
источник
3

Для справки, я лично использую, [Foo new]если я хочу, чтобы что-то в init выполнялось без использования возвращаемого значения в любом месте. Если вы не используете возврат в [[Foo alloc] init]любом месте, то вы получите предупреждение. Более или менее я использую [Foo new]для глазных конфет.

evdude100
источник
3

Я очень опоздал к этому, но я хочу упомянуть, что это новое на самом деле небезопасно в мире Obj-C со Swift. Swift создаст метод init по умолчанию, только если вы не создадите никакой другой инициализатор. Вызов new для класса swift с пользовательским инициализатором вызовет сбой. Если вы используете alloc / init, то компилятор будет правильно жаловаться, что init не существует.

MurderDev
источник
1

Если new сделает эту работу за вас, то он также сделает ваш код немного скромнее. Если бы вы в противном случае вызывали [[SomeClass alloc] init]во многих различных местах вашего кода, вы создадите «горячую точку» в реализации new, то есть во время выполнения objc, что уменьшит количество ошибок вашего кэша.

В моем понимании, если вам нужно использовать пользовательский инициализатор использования [[SomeClass alloc] initCustom].

Если нет, используйте [SomeClass new].

Майк Кроуфорд
источник
1
Я бы поспорил с "если вам нужно использовать пользовательский инициализатор, используйте [[SomeClass alloc] initCustom]". Если вам нужен пользовательский инициализатор без каких-либо параметров, никогда не делайте того, что вы предлагаете, просто переопределите initфункцию по умолчанию и используйте ее. [[SomeClass alloc] init];Если вам нужны параметры, все равно никогда не делайте что-то подобное [[SomeClass alloc] initWith:...];. Наконец, если вы переопределите initфункцию с помощью пользовательской реализации, вы можете вызвать newпри создании объекта, и он все равно будет вызывать пользовательскую initреализацию.
dirtydanee