//main thread
self.arr = @[@1, @2, @3].mutableCopy;
for (int i = 0; i < _arr.count; i ++) {
NSLog(@"element: %@", _arr[i]);
}
//thread 2
NSMutableArray* localArr = self.arr;
//get result from server
NSArray* results = @[@8, @9, @10];
//refresh local arr
[localArr removeAllObjects];
[localArr addObjectsFromArray:results];
arr初始化之后,以64位系统为例,其实际的内存布局分为三块:第一块是指针NSMutableArray* arr所处的位置,为8个字节。第二块是数组实际的内存区域所处的位置,为连续3个指针地址,各占8个字节一共24个字节。第三块才是@1,@2,@3这些NSNumber对象真正的内存空间。当我们调用不同的API对arr进行操作的时候,要分清楚实际是在操作哪部分内存。
thread 2这行代码实际上只是新生成了8个字节的第一类内存空间给localArr,localArr实际上还是和arr共享第二块和第三块内存区域,当在thread 2执行[localArr removeAllObjects];清理第二块内存区域的时候,如果主线程正在同时访问第二块内存区域_arr[1],就会导致crash了。这类问题的根本原因,还是在对于同一块内存区域的同时读写。
Swift有COW机制,当赋值时,会新生成一个数组实际的内存区域所处的位置,同时读写就不会出现问题
OC没有COW机制就需要我们自己copy,比如:
NSMutableArray* iterateArr = [self.arr copy];
self.arr = @[@1, @2, @3].mutableCopy;
只要是针对共享数组的操作,时刻记得copy一份新的内存区域,就可以实现手动COW的效果,这样Objective C也能在维护状态的时候,是多线程安全的。