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类对象,即使SubclassSuperclassinheritance该方法。

运行库在将load消息发送给所有类的超类对象(如果这些超类对象实现load )以及链接到的共享库中的所有类对象之后,将load消息发送到类对象。 但是你不知道你自己的可执行文件中的其他类是否已经收到了load

无论您的进程是否使用该类,您的进程加载到其地址空间中的每个类都将收到一条load消息。

您可以看到运行时如何在_class_getLoadMethodobjc-runtime-new.mm查找load方法,并直接从call_class_loads中的objc-loadmethod.mm调用它。

运行时也运行load的每个类别的load方法,即使同一类中的多个类别实现load 。 这是不寻常的。 通常情况下,如果两个类别在同一个类中定义了相同的方法,则其中一个方法将“赢”并被使用,而另一个方法将不会被调用。

initialize方法

运行时在将第一个消息( loadinitialize )发送到类对象或类的任何实例之前,会在类对象上调用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考虑的另一个问题是,如果有人将你分类,你可能会为你的类调用一次,每个子类调用一次。 如果你正在做一些设置staticvariables的事情,那么你需要警惕:使用dispatch_once()或者通过testingself == [MyClass class]