NSObject +加载和初始化 – 他们做什么?
我有兴趣了解领导开发人员重写+初始化或+加载的情况。 文档清楚地表明这些方法是由Objective-C运行时为您调用的,但是这些方法的文档都清楚地表明了这一点。 🙂
我的好奇心来自于苹果的示例代码 – MVCNetworking。 他们的模型类有一个+(void) applicationStartup
方法。 它在文件系统上做了一些维护,读取NSDefaults等等,并且在尝试了NSObject的类方法之后,看起来这个清理工作似乎可以放到+ load中。
我修改了MVCNetworking项目,将App Delegate中的调用删除到了+ applicationStartup,并将pipe家位加载到了+加载中…我的电脑没有着火,但这并不意味着它是正确的! 我希望了解任何细微的东西,陷阱,以及围绕自定义设置方法的东西,您必须调用vs + load或+ initialize。
对于+加载文件说:
加载消息被发送到dynamic加载和静态链接的类和类,但是只有当新加载的类或类实现了可以响应的方法时。
如果你不知道所有单词的确切含义,那么这个句子就是kludgey,很难parsing。 帮帮我!
-
“dynamic加载和静态链接”是什么意思? 什么东西可以dynamic加载和静态链接,或者它们是互斥的?
-
“…新加载的类或类实现了一个方法,可以响应”什么方法? 回应如何?
至于+初始化,文档说:
初始化它每个类只能调用一次。 如果要对类和类的类进行独立初始化,则应该实现加载方法。
我认为这个意思是,“如果你想设置类,不要使用初始化。” 好的。 什么时候或为什么我会重写初始化呢?
load
消息
运行时将load
消息发送到每个类对象,在类对象加载到进程的地址空间后不久。 对于属于程序可执行文件的类,运行时会在进程的生命周期中很早发送load
消息。 对于共享(dynamic加载)库中的类,运行时在共享库加载到进程的地址空间之后立即发送加载消息。
此外,如果该类对象本身实现了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
inheritance该方法。
运行库在将load
消息发送给所有类的超类对象(如果这些超类对象实现load
)以及链接到的共享库中的所有类对象之后,将load
消息发送到类对象。 但是你不知道你自己的可执行文件中的其他类是否已经收到了load
。
无论您的进程是否使用该类,您的进程加载到其地址空间中的每个类都将收到一条load
消息。
您可以看到运行时如何在_class_getLoadMethod
的objc-runtime-new.mm
查找load
方法,并直接从call_class_loads
中的objc-loadmethod.mm
调用它。
运行时也运行load
的每个类别的load
方法,即使同一类中的多个类别实现load
。 这是不寻常的。 通常情况下,如果两个类别在同一个类中定义了相同的方法,则其中一个方法将“赢”并被使用,而另一个方法将不会被调用。
initialize
方法
运行时在将第一个消息( load
或initialize
)发送到类对象或类的任何实例之前,会在类对象上调用initialize
方法。 这个消息是使用正常的机制发送的,所以如果你的类没有实现initialize
,而是从一个类inheritance,那么你的类将使用它的超类的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
的子类时重新初始化它自己。
运行时将在objc-initialize.mm
中的_class_initialize
函数中发送initialize
消息。 你可以看到它使用objc_msgSend
发送它,这是正常的消息发送function。
进一步阅读
查看Mike Ash周五关于这个话题的问答 。
这意味着不要在类别中覆盖+initialize
,否则可能会破坏某些东西。
+load
一旦+load
类或类别, 就会为每个实现了+load
类或类别调用一次加载。 当它说“静态链接”,这意味着编译到您的应用程序二进制文件。 这样编译的类的+load
方法将在您的应用程序启动时执行,可能在它进入main()
之前执行。 当它说“dynamic加载”时,意味着通过插件包或者调用dlopen()
来加载。 如果你在iOS上,你可以忽略这种情况。
+initialize
是在消息首次被发送给类之前调用的。 这(显然)只发生一次。 如果你在类别中覆盖+initialize
,三件事情之一将会发生:
- 你的类实现被调用,而类的实现不会
- 别人的类别实现被调用; 没有什么你写的
- 你的类别还没有被加载,它的实现永远不会被调用。
这就是为什么你不应该重写+initialize
在一个类别中 – 事实上,尝试和replace某个类别中的任何方法都是相当危险的,因为你永远不知道你正在replace什么,或者你自己的replace本身是否会被另一个replace掉类别。
顺便说一句,用+initialize
考虑的另一个问题是,如果有人将你分类,你可能会为你的类调用一次,每个子类调用一次。 如果你正在做一些设置static
variables的事情,那么你需要警惕:使用dispatch_once()
或者通过testingself == [MyClass class]
。