问题
对于某些类,我想在我的程序启动时显式调用 +initialize
方法,而不是允许运行时系统在类第一次运行时在一些不确定的点隐式调用它使用。问题是,不推荐这样做。
我的大部分类在初始化时几乎没有工作要做,所以我可以让运行时系统为这些做它的事情,但至少我的一个类需要多达 1 秒的时间在旧设备上进行初始化,而且我不希望程序启动并运行时出现断断续续的情况。 (一个很好的例子就是声音效果——我不想在第一次尝试播放声音时突然延迟。)
有哪些方法可以在启动时进行初始化?
尝试过的解决方案
我过去所做的是从 main.c
中手动调用 +initialize
方法,并确保每个 +initialize
> 方法有一个 bool initialized
变量包装在一个 @synchronized
block 中,以防止意外的双重初始化。但是现在 Xcode 警告我 +initialize
会被调用两次。不足为奇,但我不喜欢忽略警告,所以我宁愿解决这个问题。
我的下一次尝试(今天早些时候)是定义一个 +preinitialize
函数,我直接调用它而不是 +initialize
,并确保调用 +preinitialize
隐含在 +initialize
内部,以防在启动时未显式调用它。但这里的问题是 +preinitialize
内部的某些东西导致 +initialize
被运行时系统隐式调用,这让我认为这是一种非常不明智的做法。
所以假设我想将实际的初始化代码保留在 +initialize
中(它真正想要的地方)并且只写一个名为 +preinitialize
的小虚拟方法以某种方式强制 +initialize
被运行时系统隐式调用?有没有标准的方法?在单元测试中,我写了...
+ (void) preinitialize
{
id dummy = [self alloc];
NSLog(@"Preinitialized: %i", !!dummy);
}
...但在调试器中,我没有观察到 +initialize
在 +alloc
之前被调用,这表明 +initialize
是不会被 +preinitialize
内部的运行时系统隐式调用。
编辑
我找到了一个非常简单的解决方案,并将其作为答案发布。
最佳答案
运行类特定代码的第一个可能位置是 +load
,当类被添加到 ObjC 运行时时会发生这种情况。仍然不能完全确定哪些类的 +load
实现将按什么顺序调用,但是有一些规则。来自文档:
The order of initialization is as follows:
All initializers in any framework you link to.
All
+load
methods in your image.All C++ static initializers and C/C++
__attribute__(constructor)
functions in your image.All initializers in frameworks that link to you.
In addition:
A class’s
+load
method is called after all of its superclasses’+load
methods.A category
+load
method is called after the class’s own+load
method.
因此,两个对等类(例如,两个直接 NSObject
子类)都将在上面的步骤 2 中同时 +load
,但是不能保证它们两个将按哪个顺序彼此相对。
正因为如此,并且因为 ObjC 中的元类对象通常不是设置和维护状态的好地方,您可能需要其他东西......
更好的解决方案?
例如,您的“全局”状态可以保存在单例类的(单个)实例中。客户端可以调用 [MySingletonClass sharedSingleton]
来获取该实例,而不关心它是在当时还是更早完成初始设置。如果客户需要确保它更早发生(并且相对于其他事物以确定的顺序),他们可以在他们选择的时间调用该方法——比如在启动NSApplication
/UIApplication
运行循环。
备选方案
如果您不希望这种代价高昂的初始化工作发生在应用程序启动时,并且您不希望它发生在类被投入使用时,您还有一些其他选择。
- 将代码保留在
+initialize
中,并设法确保类在第一次“真正”使用之前得到消息。例如,也许您可以启动后台线程以从application:didFinishLaunching:
创建和初始化该类的虚拟实例。 - 将该代码放在其他地方 - 在类对象中或在单例中,但无论如何都在你自己创建的方法中 - 并在足够晚的时间直接调用它以进行设置以避免减慢应用程序启动但又足够快在你的类(class)需要“真正的”工作之前完成。
关于Objective-C:如何在启动时强制调用 `+initialize` 而不是在类碰巧第一次使用时调用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34799211/