1. +load与+initialize方法在多线程环境下可能遇到的问题
- +load方法:虽然
+load
方法在程序启动时由runtime加载类和分类时调用,并且保证每个类和分类的+load
方法在程序生命周期内只被调用一次。但在多线程环境下,多个类的+load
方法可能会并行调用,如果这些+load
方法中有对共享资源的操作(如全局变量的初始化等),可能会导致数据竞争问题。
- +initialize方法:
+initialize
方法在类或其子类收到第一条消息时被调用,同样保证只调用一次。在多线程环境下,多个线程可能同时尝试访问一个尚未初始化的类,从而导致+initialize
方法被多次尝试调用,虽然最终只会执行一次,但可能会有不必要的竞争和开销。
2. 优化方案及优缺点分析
方案一:使用dispatch_once
- 实现方式:
在
+initialize
方法中使用dispatch_once
来确保初始化代码只执行一次。例如:
+ (void)initialize {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 类的初始化代码
});
}
- 优点:
- 简单高效,GCD的
dispatch_once
内部使用了高效的自旋锁等机制,能确保代码只执行一次,并且性能开销较小。
- 线程安全,无论是单核还是多核环境下都能正确工作,避免了多线程竞争导致的多次初始化问题。
- 缺点:
- 只能用于
+initialize
方法,对于+load
方法不适用,因为+load
方法调用时机在dispatch_once
机制生效之前。
- 对于
+load
方法中共享资源的竞争问题无法解决。
方案二:使用互斥锁(如pthread_mutex)
- 实现方式:
在
+load
和+initialize
方法中,如果有对共享资源的操作,使用互斥锁来保护共享资源。例如:
static pthread_mutex_t loadMutex;
+ (void)load {
pthread_mutex_init(&loadMutex, NULL);
pthread_mutex_lock(&loadMutex);
// 对共享资源的操作
pthread_mutex_unlock(&loadMutex);
pthread_mutex_destroy(&loadMutex);
}
static pthread_mutex_t initializeMutex;
+ (void)initialize {
pthread_mutex_init(&initializeMutex, NULL);
pthread_mutex_lock(&initializeMutex);
static BOOL initialized = NO;
if (!initialized) {
// 类的初始化代码
initialized = YES;
}
pthread_mutex_unlock(&initializeMutex);
pthread_mutex_destroy(&initializeMutex);
}
- 优点:
- 通用性强,既可以用于
+load
方法也可以用于+initialize
方法,能有效解决共享资源的竞争问题。
- 可以精确控制对共享资源的访问,确保线程安全。
- 缺点:
- 性能开销相对较大,互斥锁的加锁和解锁操作会带来一定的性能损耗,尤其是在高并发场景下。
- 代码复杂度增加,需要手动管理互斥锁的初始化、加锁、解锁和销毁操作,容易出错。
方案三:将共享资源初始化移到单例中
- 实现方式:
将
+load
或+initialize
方法中对共享资源的初始化操作移到一个单例类中,并在单例类的sharedInstance
方法中使用dispatch_once
进行初始化。例如:
@interface SharedResource : NSObject
@property (nonatomic, strong) id resource;
+ (instancetype)sharedInstance;
@end
@implementation SharedResource
+ (instancetype)sharedInstance {
static SharedResource *shared = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
shared = [[self alloc] init];
shared.resource = [[SomeClass alloc] init]; // 共享资源初始化
});
return shared;
}
@end
+ (void)load {
// 无需对共享资源初始化,可直接使用单例
SharedResource *shared = [SharedResource sharedInstance];
// 使用共享资源
}
+ (void)initialize {
// 同样可直接使用单例获取共享资源
SharedResource *shared = [SharedResource sharedInstance];
// 使用共享资源
}
- 优点:
- 利用
dispatch_once
的高效性确保共享资源只初始化一次,性能较好。
- 代码结构清晰,将共享资源的管理集中到单例类中,便于维护和扩展。
- 缺点:
- 增加了单例类的设计和实现,对于简单场景可能有些过度设计。
- 依赖单例模式,如果项目中不适合使用单例模式,此方案不适用。