Objective-C对未初始化或重赋值的实例变量用@synchronized是否安全?
你当前的写法既不合法也不安全,无法解决多线程竞态问题,建议改为使用self或生命周期与当前类实例绑定的独立对象作为锁对象。
现有方案的两个核心风险
- 对未初始化的
nil对象加锁无任何效果:Objective-C中@synchronized的参数如果为nil,同步块会直接失去互斥能力,等同于没有加锁。你第一次调用reinitialize方法时,sharedState尚未赋值为有效值,此时加锁完全无法防止其他线程同时操作,竞态问题依然存在。 - 更换锁对象指针会直接破坏互斥逻辑:
@synchronized是对传入的对象实例加锁,而非对指针变量本身加锁。当你在同步块内将sharedState指向新的字典实例后,后续其他线程进入同步块时会拿到新的对象作为锁,和之前正在执行的同步块使用的锁完全无关,无法起到互斥作用。比如线程A持有旧锁正在读写sharedState,线程B调用reinitialize更换了指针后,线程C再进入同步块使用新锁,就会和线程A同时执行操作,直接触发崩溃。
正确实现方案
你可以直接使用self作为锁对象,保证所有操作使用的锁在实例生命周期内不会变化,所有读写sharedState的代码、包括reinitialize都包裹在同一个@synchronized(self)块中即可,示例代码如下:
@interface MyClass: NSObject { NSMutableDictionary *sharedState; } @end @implementation MyClass - (instancetype)init { self = [super init]; if (self) { [self reinitialize]; } return self; } - (void)reinitialize { @synchronized (self) { sharedState = [[NSMutableDictionary alloc] init]; } } // 读取操作示例 - (id)getValueForKey:(NSString *)key { @synchronized (self) { return sharedState[key]; } } // 修改操作示例 - (void)setValue:(id)value forKey:(NSString *)key { @synchronized (self) { sharedState[key] = value; } } @end
如果你的类有大量外部同时操作的场景,担心self作为锁会和外部的@synchronized(self)操作产生不必要的互斥,也可以单独声明一个私有锁实例变量,锁的粒度更可控,不会和类外部的锁产生冲突:
@interface MyClass: NSObject { NSMutableDictionary *sharedState; id _lock; } @end @implementation MyClass - (instancetype)init { self = [super init]; if (self) { _lock = [[NSObject alloc] init]; [self reinitialize]; } return self; } - (void)reinitialize { @synchronized (_lock) { sharedState = [[NSMutableDictionary alloc] init]; } } // 其余操作都使用@synchronized(_lock)包裹 @end
内容的提问来源于stack exchange,提问作者bdesham
相关产品推荐
相关产品推荐

