我对了解导致开发人员覆盖+初始化或+加载的情况很感兴趣。文档清楚地说明了这些方法是由Objective-C运行时为您调用的,但从这些方法的文档中可以清楚地看到这一点。:-)
我的好奇心来自于查看苹果的示例代码- MVCNetworking。他们的模型类有一个+(void) applicationStartup
方法。它在文件系统上做一些内务处理,读取NSDefaults,等等。并且,在尝试了NSObject的类方法之后,似乎可以将这项看门人的工作放到+load中。
我确实修改了MVCNetworking项目,删除了App Delegate中对+applicationStartup的调用,并将内务部分放入了+load……我的电脑没有着火,但这并不意味着它是正确的!我希望能够理解关于自定义设置方法的任何微妙之处,以及您必须调用的自定义设置方法与+load或+initialize之间的关系。
For +load文档表示:
加载消息将发送到动态加载和静态链接的类和类别,但前提是新加载的类或类别实现了可以响应的方法。
如果你不知道所有单词的确切含义,这个句子就很难解析。帮助!
至于+初始化,文档说:
初始化每个类只调用一次。如果要对类和类的类别执行独立的初始化,则应实现load方法。
我认为这意味着,“如果你试图设置这个类……不要使用initialize。”好吧,好吧。那么我什么时候或者为什么要覆盖initialize呢?
发布于 2012-11-11 06:23:06
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
运行库向Superclass
类对象发送load
消息。它不会向Subclass
类对象发送load
消息,即使Subclass
继承了Superclass
的方法也是如此。
运行库在将load
消息发送到类的所有超类对象(如果这些超类对象实现了load
)和您链接到的共享库中的所有类对象之后,将load
消息发送到类对象。但是您还不知道您自己的可执行文件中的哪些其他类已经收到了load
。
您的进程加载到其地址空间中的每个类都将收到一条load
消息,只要它实现了load
方法,而不管您的进程是否使用该类的任何其他用途。
您可以看到运行时如何在objc-runtime-new.mm
的_class_getLoadMethod
中将load
方法作为一个特例进行查找,并在objc-loadmethod.mm
中直接从call_class_loads
调用它。
运行库还会运行它加载的每个类别的load
方法,即使同一个类上的多个类别实现了load
。“这是不寻常的。通常情况下,如果两个类别在同一个类上定义了相同的方法,则其中一个方法将“获胜”并被使用,而另一个方法将永远不会被调用。
initialize
方法
运行库在将第一条消息( load
或initialize
除外)发送到类对象或类的任何实例之前,对类对象调用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
}
}
这种模式的要点是,当它有一个不实现initialize
的子类时,避免Someclass
重新初始化自身。
运行库在objc-initialize.mm
的_class_initialize
函数中发送initialize
消息。您可以看到它使用objc_msgSend
发送消息,这是正常的消息发送函数。
进一步阅读
请查看有关此主题的Mike Ash's Friday Q&A。
发布于 2012-11-11 06:28:50
它的意思是不要覆盖类别中的+initialize
,你可能会破坏一些东西。
只要加载了实现+load
的类或类别,就会对该类或类别调用一次+load
。当它说“静态链接”时,它意味着编译到你的应用程序二进制文件中。这样编译的类上的+load
方法将在应用程序启动时执行,可能在应用程序进入main()
之前执行。当它说“动态加载”时,它意味着通过插件包或调用dlopen()
加载。如果你用的是iOS,你可以忽略这种情况。
+initialize
在第一次向该类发送消息时被调用,就在该类处理该消息之前。这(显然)只会发生一次。如果覆盖类别中的+initialize
,将发生以下三种情况之一:
这就是为什么你永远不应该覆盖一个类别中的+initialize
-事实上,尝试替换一个类别中的任何方法都是非常危险的,因为你永远不能确定你要替换的是什么,或者你自己的替换本身是否会被另一个类别替换掉。
顺便说一句,使用+initialize
要考虑的另一个问题是,如果有人对你进行子类化,你的类可能会被调用一次,每个子类都会被调用一次。如果您正在执行诸如设置static
变量之类的操作,则需要防止发生这种情况:使用dispatch_once()
或测试self == [MyClass class]
。
https://stackoverflow.com/questions/13326435
复制相似问题