这个应该是全平台都会遇到的问题了。当某个对象会被多个线程修改的时候,有可能一个线程访问这个对象的时候另一个线程已经把它删掉了,导致 Crash。比较常见的是在网络任务队列里面,主线程往队列里面加入任务,网络线程同时进行删除操作导致挂掉。
例子
这个真要写比较完整的并发操作的例子就有点复杂了。
解决方法
- 加锁
- NSLock
普通的锁,加锁的时候 lock,解锁调用 unlock。
- (void)addPlayer:(Player *)player { if (player == nil) return; NSLock* aLock = [[NSLock alloc] init]; [aLock lock]; [players addObject:player]; [aLock unlock]; } }
可以使用标记符 @synchronized 简化代码:
- (void)addPlayer:(Player *)player { if (player == nil) return; @synchronized(players) { [players addObject:player]; } }
- NSRecursiveLock 递归锁
使用普通的 NSLock 如果在递归的情况下或者重复加锁的情况下,自己跟自己抢资源导致死锁。Cocoa 提供了 NSRecursiveLock 锁可以多次加锁而不会死锁,只要 unlock 次数跟 lock 次数一样就行了。
- NSConditionLock 条件锁
多数情况下锁是不需要关心什么条件下 unlock 的,要用的时候锁上,用完了就 unlock 就完了。Cocoa 提供这种条件锁,可以在满足某种条件下才解锁。这个锁的 lock 和 unlock, lockWhenCondition 是随意组合的,可以不用对应起来。
- NSDistributedLock 分布式锁
这是用在多进程之间共享资源的锁,对 iOS 来说暂时没用处。
- NSLock
-
无锁
放弃加锁,采用原子操作,编写无锁队列解决多线程同步的问题。酷壳有篇介绍无锁队列的文章可以参考一下:无锁队列的实现 - 使用其他备选方案代替多线程:Operation Objects, GCD, Idle-time notifications, Asynchronous functions, Timers, Separate processes。