面试题答案
一键面试atomic属性在内存管理方面的局限性
- 非线程安全的复合操作:虽然atomic保证了单个属性访问的原子性,但对于涉及多个属性访问的复合操作,依然不是线程安全的。例如,假设一个类有两个属性
count
和total
,在多线程环境下,如果一个操作需要先读取count
,然后根据count
的值来更新total
,即使count
和total
都用atomic修饰,这个复合操作也不是原子的,可能会出现数据竞争问题。 - 无法保证属性内部数据结构的线程安全:如果属性是一个可变的数据结构(如
NSMutableArray
),atomic只能保证对该属性本身的访问是原子的,但对于该数据结构内部的操作(如添加、删除元素),仍然不是线程安全的。
有效的解决方案
- 使用锁(
@synchronized
):可以使用@synchronized
关键字来同步代码块,确保在同一时间只有一个线程能执行该代码块。这样可以保证复合操作的线程安全性。
@interface MyClass : NSObject
@property (nonatomic, atomic, strong) NSMutableArray *dataArray;
@end
@implementation MyClass
- (void)addObjectToDataArray:(id)object {
@synchronized(self) {
[self.dataArray addObject:object];
}
}
- (id)objectFromDataArrayAtIndex:(NSUInteger)index {
id object;
@synchronized(self) {
object = self.dataArray[index];
}
return object;
}
@end
- 使用队列(
dispatch_queue_t
):通过创建串行队列,将对属性的所有操作都放到该队列中执行,这样也能保证线程安全。
@interface MyClass : NSObject
@property (nonatomic, atomic, strong) NSMutableArray *dataArray;
@end
@implementation MyClass {
dispatch_queue_t _queue;
}
- (instancetype)init {
self = [super init];
if (self) {
_queue = dispatch_queue_create("com.example.MyClassQueue", DISPATCH_QUEUE_SERIAL);
self.dataArray = [NSMutableArray array];
}
return self;
}
- (void)addObjectToDataArray:(id)object {
dispatch_sync(_queue, ^{
[self.dataArray addObject:object];
});
}
- (id)objectFromDataArrayAtIndex:(NSUInteger)index {
__block id object;
dispatch_sync(_queue, ^{
object = self.dataArray[index];
});
return object;
}
@end
- 使用线程安全的数据结构:一些第三方库提供了线程安全的数据结构,如
NSHashTable
(线程安全的集合类)。可以使用这些线程安全的数据结构来替代普通的非线程安全数据结构,从而减少手动同步的需求。
#import <Foundation/Foundation.h>
@interface MyClass : NSObject
@property (nonatomic, atomic, strong) NSHashTable *hashTable;
@end
@implementation MyClass
- (void)addObjectToHashTable:(id)object {
[self.hashTable addObject:object];
}
- (BOOL)containsObjectInHashTable:(id)object {
return [self.hashTable containsObject:object];
}
@end
这些解决方案可以有效地弥补atomic属性在内存管理方面的局限性,确保在多线程环境下数据的一致性和安全性。