NSObject+加载和+初始化-它们做什么?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (18)

我有兴趣了解领导开发人员重写+初始化或+加载的情况。文档清楚地表明这些方法是由Objective-C运行时为你调用的,但这些方法的文档都清楚地表明了这一点。

我的好奇心来自Apple的示例代码 - MVCNetworking。他们的模型类有一个+(void) applicationStartup方法。它在文件系统上执行一些内务操作,读取NSDefaults等等,并且在尝试了解NSObject的类方法之后,看起来这种管理工作似乎可以放到+加载中。

我修改了MVCNetworking项目,将应用程序委托中的调用删除到+ applicationStartup,并将家务清理工作置于+加载中......我的电脑没有着火,但这并不意味着它是正确的!我希望了解任何细微之处,陷阱和周围的自定义设置方法,你必须调用+ +加载或+初始化。

对于+加载文件说:

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

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

  • “动态加载和静态链接”是什么意思?可以动态加载和静态链接,还是它们是互斥的?
  • “...新加载的类或类别实现了一种方法,可以响应”什么方法?回应如何?

至于+初始化,文档说:

初始化它每个类只调用一次。如果你想对类和类的类进行独立的初始化,你应该实现加载方法。

我认为,“如果你想设置类,不要使用初始化。” 好的。何时或为什么我会重写初始化呢?

提问于
用户回答回答于

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_getLoadMethodobjc-runtime-new.mm,并直接调用它call_class_loadsobjc-loadmethod.mm

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

initialize方法

initialize在将第一个消息(除loadinitialize)发送到类对象或类的任何实例之前,运行时会在类对象上调用该方法。这个消息是使用正常机制发送的,所以如果你的类没有实现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_class_initialize函数中发送消息objc-initialize.mm。你可以看到它objc_msgSend用来发送它,这是正常的消息发送功能。

用户回答回答于

这意味着不要重写+initialize某个类别,否则可能会破坏某些内容。

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

+initialize被称为在消息首次处理消息之前发送给该类的消息。这(显然)只发生一次。+initialize在某个类别中重写,则会发生以下三种情况之一:

  • 你的类实现被调用,而类的实现不会
  • 别人的类别实现被调用; 没有你写的东西
  • 你的类别尚未加载,其实现从未被调用。

这就是为什么你不应该重写+initialize某个类别的原因 - 事实上,尝试和替换某个类别中的任何方法都是相当危险的,因为你永远不知道你要替换的是什么,或者你自己的替换本身是否会被另一个类别转换出来。

顺便说一句,另一个需要考虑的问题+initialize是,如果有人对你进行子类化,你可能会为你的类调用一次,每个子类调用一次。如果你正在设置static变量,那么你需要防范:dispatch_once()通过测试或通过测试self == [MyClass class]

扫码关注云+社区