Використання aloc init замість нового


344

Навчаючись Objective-C і читаючи зразок коду, я помічаю, що об'єкти зазвичай створюються за допомогою цього методу:

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

замість:

SomeObject *myObject = [SomeObject new];

Чи є причина в цьому, як я читав, що вони рівноцінні?


12
Я навіть не знав, що це можливо! Весь прочитаний нами матеріал робить вигляд, що нового ключового слова не існує!
Джонатан

78
Насправді "new" - це не ключове слово в Objective-C, але NSObject реалізує метод класу "new", який просто називає "alloc" і "init".
Дон Мак-Кагхі

3
Джонатан, саме це підштовхнуло моє запитання. Вони можуть бути функціонально еквівалентними, але [[alloc] init] явно є домінуючою ідіомою.
willc2

1
Я думаю, що Apple (з того, що я пам’ятаю з однієї з їхніх лекцій про iTunes Stanford) просто заохочує вас замість цього використовувати alloc init, щоб ви могли зрозуміти процес того, що відбувається. Вони також не використовують багато нового у своєму зразковому коді, тому здається, що 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, часом погана ідея.)
Senseful

6
@Senseful Я вважаю, що це сягає часів до 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...

5
Відмінна відповідь! Просто хочу додати, що використання + new чудово підходить для тих випадків, коли потрібно використовувати лише -init і нічого більш спеціалізованого, як -initWithSomething: ...
cgossain

На жаль, цей приклад не є доказом того, що вони однакові. Це лише приклад, коли вони дають однаковий результат.
Вінс О'Салліван

10
На жаль, цей приклад не є доказом того, що вони однакові. Це лише приклад, коли вони, мабуть, дають однаковий результат. З іншого боку, це доводить, що вони різні ... Беручи приклад вище, змініть "InitAllocNewTest.h" таким чином: @interface InitAllocNewTest: NSObject - (typetype) __unavailable init; @end [[InitAllocNewTest alloc] init]не збирається , хоча [InitAllocNewTest new]це не впливає. (Вибачення за відсутність розривів ліній тощо)
Вінс О'Салліван

1
@ VinceO'Sullivan, хороший момент. Це означає, що якщо хочеться зробити init недоступним, як у однокласному класі, слід також відключити нове. Я не зачіпаю тут, чи є одиночні добрі чи погані.
користувач3099609

У мене є питання з наведеного вище коду. чому "повернути [super init];" як правило, init - це метод, коли члени класу ініціалізуються, чому зміни вказівника дуже дивні!
Прутвідхар Рао Надунору

52

+newеквівалентний +alloc/-initв NSObjectреалізації Apple . Це малоймовірно, що це колись зміниться, але, залежно від рівня вашої параної, документація Apple на, +newздається, дозволить змінити реалізацію (і порушити її еквівалентність) у майбутньому. З цієї причини, оскільки "явне краще, ніж неявне", і для історичної спадкоємності спільнота "Об'єктив-С" взагалі уникає +new. Однак, як правило, ви можете помітити останніх прихильників Java на «Objective-C» шляхом їх використання +new.


8
Хоча це часто відповідає дійсності, існує табір на користь терміновості, і в цьому випадку явніше буде лише тоді, коли є явна і наявна стурбованість, яка цього вимагає.
Пітер ДеВіз

27
Я прихильнив це, бо існував +newз часів NeXT. Якщо що- +newнебудь є ознакою того, хто навчився objc давно; Я бачу, що багато людей, які зрозуміли мову або навіть пишуть її вже роками, але, очевидно, після буму iOS, не мають поняття, що +newозначає. По-друге, як +newце дуже давно, і з часів NeXT, Apple було б досить божевільним, щоб змінити його таким чином, що він порушує старий код, особливо зважаючи на те, що їх власні бази кодів, ймовірно, засипані ним.
asveikau

1
Я майже впевнений, що newідіома походить від Smalltalk. Він також використовується в Ruby, і Objective-C і Ruby отримують багато своїх синтаксисів і умовних позначень від Smalltalk.
Брендон Роберто

3
Він дозволяє лише змінити аллок. Документи явно стану -init будуть називатися. Тож більше залежить від того, чи перекриєш ви аллока коли-небудь.
Кендалл Гельмстеттер Гелнер

7
Я не можу реально уявити, чому рішення, де потрібно ввести БІЛЬШЕ, було б кращим (alloc init), особливо в такій багатослівній мові, як Objective-C, де економія натискань клавіш заощадить ваш час. Ці "розроблені розробники Java" просто використовують свої аналітичні навички, щоб витрачати менше часу на написання коду, а не зайво вводити зайві символи, які дають той же функціональний результат.
DiscDev

9

Часто вам потрібно буде передавати аргументи initі тому ви будете використовувати інший метод, наприклад [[SomeObject alloc] initWithString: @"Foo"]. Якщо ви звикли писати це, ви звикаєте робити це так, і, [[SomeObject alloc] init]можливо , це стане природніше [SomeObject new].


7

Один короткий відповідь:

  1. Обидва однакові. Але
  2. 'new' працює лише з базовим ініціалізатором 'init' і не працюватиме з іншими ініціалізаторами (наприклад, initWithString :).

3

Для побічної записки я особисто використовую, [Foo new]якщо хочу зробити щось у "init", не використовуючи його повернення ніде. Якщо ви не скористаєтесь поверненням з [[Foo alloc] init]будь- якого місця, то отримаєте попередження. Більш-менш я використовую [Foo new]для очей цукерки.


3

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


1

Якщо нове зробить роботу для вас, то це зробить ваш код також значно меншим. Якщо ви в іншому випадку зателефонуєте [[SomeClass alloc] init]в багато різних місць свого коду, ви створите "Гарячу точку" в новій реалізації - тобто в режимі виконання objc - це зменшить кількість ваших пропусків кеша.

Наскільки я розумію, якщо вам потрібно використовувати користувацький ініціалізатор [[SomeClass alloc] initCustom].

Якщо ви цього не зробите, використовуйте [SomeClass new].


1
Я б заперечував із тим, "якщо вам потрібно використовувати користувальницький ініціалізатор [[SomeClass alloc] initCustom]". Якщо вам потрібен користувальницький ініціалізатор без будь-яких параметрів, ніколи не робіть щось подібне, що ви пропонуєте, просто замініть функцію за замовчуванням initі використовуйте її. [[SomeClass alloc] init];Якщо вам потрібні параметри, все одно ніколи не робіть нічого подібного, робіть [[SomeClass alloc] initWith:...];. Нарешті, якщо ви перекриєте initфункцію за допомогою спеціальної реалізації, ви можете зателефонувати newпри створенні об'єкта, і він все одно буде викликати користувацьку initреалізацію.
бруднене
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.