首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >NSObject +load和+initialize -它们做什么?

NSObject +load和+initialize -它们做什么?
EN

Stack Overflow用户
提问于 2012-11-11 05:53:51
回答 2查看 43.1K关注 0票数 122

我对了解导致开发人员覆盖+初始化或+加载的情况很感兴趣。文档清楚地说明了这些方法是由Objective-C运行时为您调用的,但从这些方法的文档中可以清楚地看到这一点。:-)

我的好奇心来自于查看苹果的示例代码- MVCNetworking。他们的模型类有一个+(void) applicationStartup方法。它在文件系统上做一些内务处理,读取NSDefaults,等等。并且,在尝试了NSObject的类方法之后,似乎可以将这项看门人的工作放到+load中。

我确实修改了MVCNetworking项目,删除了App Delegate中对+applicationStartup的调用,并将内务部分放入了+load……我的电脑没有着火,但这并不意味着它是正确的!我希望能够理解关于自定义设置方法的任何微妙之处,以及您必须调用的自定义设置方法与+load或+initialize之间的关系。

For +load文档表示:

加载消息将发送到动态加载和静态链接的类和类别,但前提是新加载的类或类别实现了可以响应的方法。

如果你不知道所有单词的确切含义,这个句子就很难解析。帮助!

  • “动态加载和静态链接”是什么意思?某些东西可以动态加载和静态链接吗,或者它们是否相互exclusive?
  • "...the新加载的类或类别实现了一个可以响应“What method?如何回应?

至于+初始化,文档说:

初始化每个类只调用一次。如果要对类和类的类别执行独立的初始化,则应实现load方法。

我认为这意味着,“如果你试图设置这个类……不要使用initialize。”好吧,好吧。那么我什么时候或者为什么要覆盖initialize呢?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2012-11-11 06:23:06

load消息

在类对象加载到进程的地址空间后不久,运行库将load消息发送到每个类对象。对于属于程序可执行文件的类,运行库在进程生命周期的早期发送load消息。对于共享(动态加载)库中的类,运行库在将共享库加载到进程的地址空间后立即发送加载消息。

此外,只有类对象本身实现了load方法,运行库才会向该类对象发送load。示例:

代码语言:javascript
复制
@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方法

运行库在将第一条消息( loadinitialize除外)发送到类对象或类的任何实例之前,对类对象调用initialize方法。这条消息是使用普通机制发送的,所以如果您的类没有实现initialize,而是继承自一个实现了的类,那么您的类将使用它的超类的initialize。运行库将首先向类的所有超类发送initialize (如果超类尚未发送initialize)。

示例:

代码语言:javascript
复制
@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;
}

此程序打印两行输出:

代码语言:javascript
复制
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的规范方法是:

代码语言:javascript
复制
@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

票数 192
EN

Stack Overflow用户

发布于 2012-11-11 06:28:50

它的意思是不要覆盖类别中的+initialize,你可能会破坏一些东西。

只要加载了实现+load的类或类别,就会对该类或类别调用一次+load。当它说“静态链接”时,它意味着编译到你的应用程序二进制文件中。这样编译的类上的+load方法将在应用程序启动时执行,可能在应用程序进入main()之前执行。当它说“动态加载”时,它意味着通过插件包或调用dlopen()加载。如果你用的是iOS,你可以忽略这种情况。

+initialize在第一次向该类发送消息时被调用,就在该类处理该消息之前。这(显然)只会发生一次。如果覆盖类别中的+initialize,将发生以下三种情况之一:

  • 你的类别实现会被调用,类的实现也会被调用;
  • 你写的任何东西都不会加载你的类别,而且它的实现永远不会被调用。

这就是为什么你永远不应该覆盖一个类别中的+initialize -事实上,尝试替换一个类别中的任何方法都是非常危险的,因为你永远不能确定你要替换的是什么,或者你自己的替换本身是否会被另一个类别替换掉。

顺便说一句,使用+initialize要考虑的另一个问题是,如果有人对你进行子类化,你的类可能会被调用一次,每个子类都会被调用一次。如果您正在执行诸如设置static变量之类的操作,则需要防止发生这种情况:使用dispatch_once()或测试self == [MyClass class]

票数 17
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/13326435

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档