Почему alloc и init вызываются отдельно в Objective-C?

Примечание. Я относительно новичок в Objective-C и прихожу из Java и PHP.

Может кто-нибудь объяснить мне, почему я всегда должен сначала выделить, а затем инициализировать экземпляр?

Невозможно это сделать в методах init, подобных этому:

+ (MyClass*)init {
    MyClass *instance = [MyClass alloc];
    [instance setFoo:@"bla"];

    return instance;
}

+ (MyClass*)initWithString:(NSString*)text {
    MyClass *instance = [MyClass init];
    [instance setFoo:text];

    return instance;
}
...

Это просто реликт из старых дней C или есть что-то, чего я не вижу?

Я знаю, что это не проблема, так как я мог бы всегда вызывать alloc и init, но, поскольку это немного утомительно, мне бы хотелось хотя бы знать, почему я это делаю.

Мне нравится выразительность языка, но это то, что я хочу полностью понять, чтобы подумать о способе Objective-C.

Спасибо!

Ответ 1

+ new завершает отправку сообщения + alloc классу и сообщение -init на все, что возвращается из + alloc.

Причина, по которой NeXT отошла от соглашения Stepstone с использованием нового сообщения (которое было идеей Smalltalk), заключается в том, что в начале они сталкивались с ситуациями, когда они хотели иметь возможность инициализировать один и тот же объект более одного раза.

Ответ 2

Поскольку создание экземпляра и инициализация экземпляра - это два отдельных задания.

Вы отправляете сообщение alloc в класс, чтобы получить неинициализированный экземпляр. Затем вы должны инициализировать экземпляр, и у вас часто есть несколько способов сделать это. Например:

myStr = [[NSString alloc] init]; //Empty string
myStr = [[NSString alloc] initWithFormat:@"%@.%@", parentKeyPath, key];
myStr = [[NSString alloc] initWithData:utf16data encoding:NSUnicodeStringEncoding error:&error];
myStr = [[NSString alloc] initWithContentsOfURL:URL encoding:NSUTF8StringEncoding error:&error];

Каждая из них инициализирует строку совершенно другим способом. Как вы инициализируете строку, зависит от того, от чего вы хотите ее инициализировать.

Конечно, никто не любит писать alloc, а затем init, а затем autorelease каждый раз, поэтому обычно у вас есть методы удобства (например, stringWithFormat:), которые выполняют все три шага для вас.

Изменить: Более подробно об этой теме, в том числе о важных комментариях комментаторов, см. мой блог " Reunification".

Ответ 3

См. NSZone.

+alloc - сокращение для +allocWithZone:, которое является механизмом Cocoa, обеспечивает оптимизацию выделения памяти.

Итак, у вас есть возможность сделать что-то вроде этого:

foo = [[NSString allocWithZone:MyZone] initWithString:@"Foo"];
foo2 = [foo copyWithZone:MyZone];

Идея зон памяти заключается в том, что если у вас есть большое количество похожих объектов, которые часто выделяются и освобождаются, более эффективно использовать отдельную зону памяти для этих объектов.

Для того, чтобы зонирование было эффективным, вы хотели бы иметь +allocWithZone: для каждого подкласса NSObject, поэтому вам нужно разделить выделение и инициализацию. Вы можете создавать и использовать все ярлыки, которые вы хотите, например +new, но под ним все, что вам нужно, метод -init, который инициализирует объект, который уже был выделен.

Ответ 4

"Разделение этапов выделения и инициализации создания экземпляра дает много преимуществ. Его можно использовать любые варианты метода класса + alloc для размещения экземпляра, а затем использовать любой доступный инициализатор с новым экземпляром. Это позволяет создавать ваши собственные методы инициализации без необходимости предоставления альтернативных реализаций всех методов распределения. Новые методы распределения редко создаются, потому что существующие методы удовлетворяют практически любую потребность. Однако для каждого класса создается один или несколько новых инициализаторов. Из-за разделения этапов выделения и инициализации реализация инициализатора должна иметь дело только с переменными новых экземпляров и может полностью игнорировать распределяемые проблемы. Разделение упрощает процесс написания инициализаторов. Кроме того, Cocoa стандартные инициализаторы, такие как -initWithCoder: работают с экземплярами независимо от способа выделения памяти для экземпляра. Одним из негативных последствий разделения выделения и инициализации является необходимость знать о соглашениях, таких как назначенный инициализатор. Вы должны знать, какие методы обозначаются инициализаторами и как создавать и документировать новые инициализаторы в подклассах. В конечном счете использование назначенных инициализаторов упрощает разработку программного обеспечения, но есть аргумент в пользу того, что шаблон Two-Stage Creation добавляет к ранней кривой обучения для разработчиков Cocoa.


(c) Cocoa Шаблоны проектирования Эрика М. Бака и Дональда А. Яктмана

Ответ 5

Вам не обязательно. Вы можете использовать [MyClass new]. Это похоже на ваш гипотетический метод init.

В принципе, Objective-C, который первоначально не имел сбор мусора, разделяет концепцию выделения памяти и инициализации класса. Вот почему существуют два разных метода. Когда вы вызываете alloc, вы явно выделяете память.

Ответ 6

В большинстве классов есть то, о чем вы просите. Вы уже получили ответы на вопрос, почему это так, и почему вы не всегда хотите использовать это все время, но если вы прочитаете документацию по классам, вы увидите много методов класса, которые будут действовать таким образом, и они часто используются.

Для NSString у вас есть, например:

+ (id)string  // (Empty string)
+ (id)stringWithFormat:...  // Formatted string (like you use)
+ (id)stringWithContentsOfURL:... // String populated with contents of URL

И так далее. И тогда вы использовали бы это как: NSString *myString = [NSString stringWithFormat:@"Hello %@\n", userName];

Большинство других классов имеют это, например NSArray:

+ (id)array
+ (id)arrayWithContentsOfFile:...
+ (id)arrayWithContentsOfURL:...
+ (id)arrayWithObjects:...

Вам просто нужно прочитать документацию.:) И прочитайте другие ответы о том, почему вы не хотите использовать это слишком много.

Ответ 7

alloc: память присваивается/предоставляется объектной ссылке. Теперь ссылка имеет память, но еще ничего не сделала. Эта память пуста (самый редкий случай) или с некоторыми анонимными данными.

alloc и init: выделенная память очищается/очищается. Память инициируется нулевым битом.

alloc и initwithdata...: выделенная память инициируется с требуемыми данными, относящимися к свойствам класса.

Например, когда вы покупаете сюжет, вы получаете его. Этот сюжет дается вам так, как есть, могут быть разрушенные кирпичи или старый дом. Это alloc.

Когда вы очищаете участок и удаляете всю грязь и мусор. Это alloc с init.

Когда вы строите это в какой-то ценный дом, он становится более значимым для вас. И это alloc initwith...