NSObject + load и + initialize-что они делают?
Я заинтересован в понимании обстоятельств, приводящих разработчика к переопределению +initialize или +load. Документация дает понять, что эти методы вызываются для вас средой выполнения Objective-C, но это действительно все, что ясно из документации этих методов. : -)
мое любопытство связано с тем, что я смотрю на пример кода Apple - MVCNetworking. Их класс модели имеет +(void) applicationStartup метод. Он делает небольшую уборку в файловой системе, считывает NSDefaults и т. д. и т. п... и, после попытки использовать методы класса grok NSObject кажется, что эта вспомогательная работа может быть в порядке, чтобы поместить в +load.
Я изменил проект MVCNetworking, удалив вызов делегата приложения в + applicationStartup и поместив биты домашнего хозяйства в +load... мой компьютер не загорелся, но это не значит, что это правильно! Я надеюсь получить представление о любых тонкостях, gotchas и тому подобном вокруг пользовательского метода настройки, который вы должны вызвать versus + load или +инициализировать.
для + load документация говорит:
сообщение о загрузке отправляется в классы и категории, которые являются обоими
динамически и статически, но только если загруженные
класс или категория реализует метод, который может ответить.
это предложение является kludgey и трудно разобрать, если вы не знаете точного значения всех слов. Помогите!
что значит "как динамически и статически?"Может ли что-то быть динамически загружено и статически связано, или они взаимоисключающие?
"...недавно загруженный класс или категория реализует метод, который может ответить" какой метод? Как реагировать?
Что касается + initialize, документация говорит:
инициализации вызывается только один раз для каждого класса. Если вы хотите выполнить
независимая инициализация для класса и для категорий
класса, вы должны реализовать методы загрузки.
Я понимаю это так: "если вы пытаетесь настроить класс... не используйте инициализировать.- Ладно, ладно. Когда или почему я должен переопределить инициализацию?
2 ответов:
The
loadсреда выполнения отправляет
loadсообщение для каждого объекта класса, очень скоро после загрузки объекта класса в адресное пространство процесса. Для классов, которые являются частью исполняемого файла программы, среда выполнения отправляетloadсообщение очень рано в процессе жизни. Для классов, которые находятся в общей (динамически загружаемой) библиотеке, среда выполнения отправляет сообщение о загрузке сразу после загрузки общей библиотеки в адрес процесса пространство.кроме того, среда выполнения отправляет только
loadк объекту класса, если этот объект класса сам реализуетloadметод. Пример:@interface Superclass : NSObject @end @interface Subclass : Superclass @end @implementation Superclass + (void)load { NSLog(@"in Superclass load"); } @end @implementation Subclass // ... load not implemented in this class @endсреда выполнения отправляет
loadсообщенииSuperclassобъект класса. Это делает не отправитьloadсообщенииSubclassобъект класса, хотяSubclassнаследует метод отSuperclass.среда выполнения отправляет
loadсообщение для объекта класса после его отправки элементloadсообщение для всех объектов суперкласса класса (если эти объекты суперкласса реализацииload) и все объекты класса в общих библиотеках, на которые вы ссылаетесь. Но вы не знаете, какие другие классы в вашем собственном исполняемом файле получилиloadеще.каждый класс, который ваш процесс загружает в свое адресное пространство, получит
loadсообщение, если оно реализуетloadметод, независимо от того, делает ли ваш процесс любое другое использование класс.вы можете видеть, как среда выполнения ищет
loadметод как частный случай в_class_getLoadMethodнаobjc-runtime-new.mm, и вызывает его непосредственно изcall_class_loadsinobjc-loadmethod.mm.среда выполнения также работает
loadметод каждой категории он загружает, даже если несколько категорий в одном классе реализуютload. Это необычно. Обычно, если две категории определяют один и тот же метод в одном классе, один из методов "выиграет" и будет используется и другой метод никогда не вызывается.The
initializeметодсреда выполнения вызывает
initializeметод на объекте класса непосредственно перед отправкой первого сообщения (кромеloadилиinitialize) к объекту класса или любым экземплярам класса. Это сообщение отправлено с помощью обычного механизма, так что если ваш класс не реализуетinitialize, но наследует от класса, который делает, то ваш класс будет использовать свой суперклассinitialize. Среда выполнения будет отправлять элементinitializeсначала всем суперклассам класса (если суперклассы еще не были отправленыinitialize).пример:
@interface Superclass : NSObject @end @interface Subclass : Superclass @end @implementation Superclass + (void)initialize { NSLog(@"in Superclass initialize; self = %@", self); } @end @implementation Subclass // ... initialize not implemented in this class @end int main(int argc, char *argv[]) { @autoreleasepool { Subclass *object = [[Subclass alloc] init]; } return 0; }эта программа выводит две строки вывода:
2012-11-10 16:18:38.984 testApp[7498:c07] in Superclass initialize; self = Superclass 2012-11-10 16:18:38.987 testApp[7498:c07] in Superclass initialize; self = Subclassтак как система отправляет
initializeметод лениво, класс не получит сообщение, если ваша программа фактически не отправляет сообщения в класс (или подкласс, или экземпляры класса или подклассов). И к тому времени, как вы получитеinitialize, каждый класс в ваш процесс должен был уже получитьload(если это уместно).канонический способ реализации
initializeэто:@implementation Someclass + (void)initialize { if (self == [Someclass class]) { // do whatever } }смысл этого шаблона заключается в том, чтобы избежать
Someclassповторная инициализация себя, когда он имеет подкласс, который не реализуетinitialize.среда выполнения отправляет
initializeсообщение
что это значит, не переопределять
+initializeв категории, вы, вероятно, сломать что-то.
+loadвызывается один раз для каждого класса или категории, реализующий+load,как только этот класс или категория загружается. Когда он говорит "статически связанный", это означает, что он скомпилирован в двоичный файл вашего приложения. Элемент+loadметоды в классах, скомпилированных таким образом, будут выполняться при запуске вашего приложения, вероятно, до его вводаmain(). Когда он говорит "динамически", это означает, загружается через пакеты плагинов или вызовdlopen(). Если вы находитесь на iOS, вы можете игнорировать этот случай.
+initializeвызывается при первой отправке сообщения в класс, непосредственно перед обработкой этого сообщения. Это (очевидно) происходит только один раз. Если вы переопределите+initializeв категории произойдет одна из трех вещей:
- ваша реализация категории вызывается, а реализация класса не
- реализация чужой категории получает звонил; ничего ты не написал
- ваша категория еще не загружена, и ее реализация никогда не вызывается.
вот почему вы никогда не должны переопределить
+initializeв категории - на самом деле это довольно опасно пытаться заменить любой метод в категории, потому что вы никогда не уверены, что вы заменяете или будет ли ваша собственная замена сама по себе отключена другой категорией.кстати, еще один вопрос, чтобы рассмотреть с
+initializeЭто то, что если кто-то подклассы вас, вы потенциально получите вызов один раз для вашего класса и один раз для каждого подкласса. Если вы делаете что-то вроде настройкиstaticпеременные, вы хотите, чтобы защититься от этого: либо сdispatch_once()или путем тестированияself == [MyClass class].
Comments