Objective c 目标C:如何强制呼叫+;initialize`在启动时而不是在类第一次使用时初始化? 问题
对于某些类,我希望在程序启动时显式调用Objective c 目标C:如何强制呼叫+;initialize`在启动时而不是在类第一次使用时初始化? 问题,objective-c,initialization,startup,Objective C,Initialization,Startup,对于某些类,我希望在程序启动时显式调用+initialize方法,而不是允许运行时系统在稍后首次使用该类时的某个不确定点隐式调用它。问题是,不建议这样做 我的大多数类在初始化时几乎没有什么工作要做,所以我可以让运行时系统来完成它的工作,但至少我的一个类需要1秒钟才能在旧设备上初始化,我不希望以后程序启动并运行时出现问题。(声音效果就是一个很好的例子——我不想在第一次尝试播放声音时突然延迟。) 在启动时进行初始化的一些方法是什么 尝试的解决方案 我在过去所做的是从main.c手动调用+initia
+initialize
方法,而不是允许运行时系统在稍后首次使用该类时的某个不确定点隐式调用它。问题是,不建议这样做
我的大多数类在初始化时几乎没有什么工作要做,所以我可以让运行时系统来完成它的工作,但至少我的一个类需要1秒钟才能在旧设备上初始化,我不希望以后程序启动并运行时出现问题。(声音效果就是一个很好的例子——我不想在第一次尝试播放声音时突然延迟。)
在启动时进行初始化的一些方法是什么
尝试的解决方案
我在过去所做的是从main.c
手动调用+initialize
方法,并确保每个+initialize
方法都有一个bool initialized
变量包装在@synchronized
块中,以防止意外的双重初始化。但是现在Xcode警告我,+initialize
将被调用两次。这并不奇怪,但我不喜欢忽视警告,所以我宁愿解决这个问题
我的下一次尝试(今天早些时候)是定义一个+preinitialize
函数,我直接调用它,而不是+initialize
,并确保我在+initialize
内部隐式调用+preinitialize
,以防启动时没有显式调用它。但这里的问题是,+preinitialize
中的某些内容导致运行时系统隐式调用+initialize
,这让我认为这是一种非常不明智的方法
假设我想将实际的初始化代码保存在+initialize
(它真正想要的地方)中,然后只编写一个名为+preinitialize
的小虚拟方法,强制运行时系统以某种方式隐式调用+initialize
?有没有一个标准的方法?在单元测试中,我写道
+ (void) preinitialize
{
id dummy = [self alloc];
NSLog(@"Preinitialized: %i", !!dummy);
}
…但是在调试器中,我没有观察到在+alloc
之前调用+initialize
,这表明+initialize
内部的运行时系统没有隐式调用+preinitialize
编辑
我找到了一个非常简单的解决方案,并将其作为答案发布。运行特定于类的代码的第一个可能的位置是,当类添加到ObjC运行时,就会发生这种情况。现在还不能完全确定哪些类的
+load
实现将按什么顺序被调用,但有一些规则。从文档中:
初始化的顺序如下:
+加载图像中的方法
- 类的
方法在其所有超类的+load
方法+load
- 在类自己的
方法之后调用category+load
方法+load
NSObject
子类)都将+加载
,但不能保证它们之间的顺序是相对的
正因为如此,而且由于ObjC中的元类对象通常不是设置和维护状态的好地方,所以您可能需要其他东西
更好的解决方案?
例如,您的“全局”状态可以保存在单例类的(单个)实例中。客户端可以调用[MySingletonClass sharedSingleton]
来获取该实例,而不关心它是在那个时候还是更早的时候完成了初始设置。如果客户机需要确保它发生得更早(并且以相对于其他事物的确定顺序),他们可以在自己选择的时间调用该方法-例如在main
中,然后启动NSApplication
/UIApplication
运行循环
选择
如果您不希望这种昂贵的初始化工作在应用程序启动时发生,也不希望在类投入使用时发生,那么您还有一些其他选择
- 将代码保存在
中,并设法确保类在第一次“真正”使用之前收到消息。例如,您可以启动一个后台线程,从+initialize
创建并初始化该类的虚拟实例应用程序:didFinishLaunching:
- 将代码放在其他地方-在类对象或单例中,但无论如何都要放在自己创建的方法中-并在足够晚的时间直接调用它,以便安装程序避免减慢应用程序启动,但足够快,以便在需要类的“真正”工作之前完成
+initialize
的设置点,该设置点在程序启动后不久调用。这是一个奇怪的环境:依赖+load
的其他类可能尚未完全初始化,更重要的是,您的main()
!这意味着没有自动释放池
在加载
之后但在初始化
之前,将调用标有\uuuu属性((构造函数))
的函数。就我所知,这不允许你做很多在main()
中做不到的事情
一个选项是在main()
或
@interface Foo: NSObject
+ (void)setup;
@end
@implementation Foo
+ (void)setup {
NSLog(@"Setup start");
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"Setup running");
[NSThread sleepForTimeInterval:1]; // Expensive op
});
}
@end
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSLog(@"START");
// Here, you should setup your UI into an "inactive" state, since we can't do things until
// we're done initializing.
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
[Foo setup];
// And any other things that need to intialize in order.
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"We're all ready to go now! Turn on the the UI. Set the variables. Do the thing.");
});
return YES;
}
#if !defined(NS_BLOCK_ASSERTIONS)
#define FooAssertInitialized(condition) NSAssert([Foo isInitialized], @"You must call +setup before using Foo.")
#else
#define FooAssertInitialized(condition)
#endif
- (void)someMethodThatRequiresInitialization {
FooAssertInitialized();
// Do stuff
}
(void)[MyClass class];
+ (void) preinitialize
{
// Simply by calling this function at startup, an implicit call to
// +initialize is generated.
}
[MyClass preinitialize]; // Implicitly invokes +initialize.