面试题答案
一键面试多线程环境下NSArray和NSDictionary可能出现的问题
- 数据竞争:多个线程同时读写集合,可能导致数据不一致。例如,一个线程正在遍历NSArray,另一个线程同时对其进行添加或删除操作,可能会引发崩溃或未定义行为。
- 内存管理问题:不同线程对集合内对象的内存管理操作不一致,可能导致对象过早释放或内存泄漏。
通过锁机制保证线程安全
- 使用NSLock:
NSLock *arrayLock = [[NSLock alloc] init];
NSMutableArray *myArray = [NSMutableArray array];
// 线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[arrayLock lock];
[myArray addObject:@"Object 1"];
[arrayLock unlock];
});
// 线程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[arrayLock lock];
id obj = [myArray objectAtIndex:0];
NSLog(@"Object: %@", obj);
[arrayLock unlock];
});
分析:NSLock是一种简单的互斥锁,通过lock
和unlock
方法来保护对myArray
的操作,确保同一时间只有一个线程能访问和修改数组,从而保证数据一致性。
- 使用@synchronized:
NSMutableDictionary *myDict = [NSMutableDictionary dictionary];
// 线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@synchronized (myDict) {
[myDict setObject:@"Value 1" forKey:@"Key 1"];
}
});
// 线程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@synchronized (myDict) {
id value = [myDict objectForKey:@"Key 1"];
NSLog(@"Value: %@", value);
}
});
分析:@synchronized
块会自动为传入的对象(这里是myDict
)创建一个锁,在块内的代码执行期间,其他线程无法进入相同对象的synchronized
块,保证了对字典操作的线程安全。
通过GCD保证线程安全
- 使用串行队列:
dispatch_queue_t serialQueue = dispatch_queue_create("com.example.serialQueue", DISPATCH_QUEUE_SERIAL);
NSMutableArray *myArray = [NSMutableArray array];
// 线程1
dispatch_async(serialQueue, ^{
[myArray addObject:@"Object 1"];
});
// 线程2
dispatch_async(serialQueue, ^{
id obj = [myArray objectAtIndex:0];
NSLog(@"Object: %@", obj);
});
分析:通过创建一个串行队列,所有提交到该队列的任务会顺序执行,避免了多个线程同时操作myArray
,从而保证了线程安全。
- 使用队列同步:
NSMutableDictionary *myDict = [NSMutableDictionary dictionary];
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t syncQueue = dispatch_queue_create("com.example.syncQueue", DISPATCH_QUEUE_CONCURRENT);
// 线程1
dispatch_async(globalQueue, ^{
dispatch_barrier_async(syncQueue, ^{
[myDict setObject:@"Value 1" forKey:@"Key 1"];
});
});
// 线程2
dispatch_async(globalQueue, ^{
dispatch_sync(syncQueue, ^{
id value = [myDict objectForKey:@"Key 1"];
NSLog(@"Value: %@", value);
});
});
分析:dispatch_barrier_async
会等待之前提交到syncQueue
的任务执行完毕,并且在它执行期间,其他并行任务无法进入syncQueue
,保证了写操作的原子性。dispatch_sync
则用于同步读取操作,确保读取时数据的一致性。