Objective-C:如何在启动时强制调用 `+initialize` 而不是在类碰巧第一次使用时调用?

标签 objective-c initialization startup

问题

对于某些类,我想在我的程序启动时显式调用 +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:

  1. All initializers in any framework you link to.

  2. All +load methods in your image.

  3. All C++ static initializers and C/C++ __attribute__(constructor) functions in your image.

  4. 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/

相关文章:

jsf - JSF 中有类似 ServletContextListener 的东西吗?

ubuntu - 命令未在 etc/rc.local ubuntu 中运行

ios - JSON 文本未以数组或对象开头,并且未设置允许片段的选项

iphone - 了解用于查找内存泄漏的 'leaks' 命令行实用程序

c - 结构体数组的初始化

android - 基于 Android 示例的 Flutter 应用程序在 BOOT_COMPLETED 上自动启动不起作用

iphone - 我们应该手动释放 NSDate 吗?

ios - 是否可以在WEBUI 的链接中拦截点击处理程序,并添加自定义点击操作?

c++ - C++ 和 C 中的 union 初始化

java - 如何在单独的线程中进行单例的急切初始化?