iOS如何实现一个线程安全的 NSMutableArray?

NSMutableArray是线程不安全的,当有多个线程同时对数组进行操作的时候可能导致崩溃或数据错误

线程锁:使用线程锁对数组读写时进行加锁

派发队列:在《Effective Objective-C 2.0..》书中第41条:多用派发队列,少用同步锁中指出:使用“串行同步队列”(serial synchronization queue),将读取操作及写入操作都安排在同一个队列里,即可保证数据同步。而通过并发队列,结合GCD的栅栏块(barrier)来不仅实现数据同步线程安全,还比串行同步队列方式更高效。

方案二
iOS-SDK只提供了非线程安全的数组。如果要多线程并发的使用一个数组对象就必须要加锁,频繁的加锁使得代码的调用非常的麻烦。

我们需要多线程的读写锁在类的内部实现,所以需要对NSMutableArray进行封装,封装后的对象负责接受所有事件并将其转发给真正的NSMutableArray对象,并通过合理的调度使得其支持多线程并发。

1 新建一个对象来对NSMutableArray 数组进行封装,包含

dispatch_queue_t 调度队列对象 和一个

NSObject 具体操作对象作为成员变量

并且初始化操作对象

- (id)init {
  self = [super init];
  if (self) {
    self.container = [NSMutableArray array];
   }
   return self;
}

2.回到JXMultiThreadObject类中 利用下面方法对一个对象无法实现的方法进行拦截和派发

- (void)forwardInvocation:(NSInvocation *)anInvocation {
}

该方法当你调用了一个对象没有实现的方法时,forwardInvocation方法将会响应,并让你觉得这个方法的处理方式,

在这之前你需要先实现

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
   return [[_container class] instanceMethodSignatureForSelector:aSelector];
}

才能激活forwardInvocation

3.这里利用到G-C-D的调度机制对对象和对象的行为进行调度

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    dispatch_barrier_sync(_dispatchQueue, ^{
        [anInvocation invokeWithTarget:_container];
    });
}

这里使用了同步的阻塞调度,属于效率比较低的一种调度方式,可以简单地作一下优化

- (void)forwardInvocation:(NSInvocation *)anInvocation {
NSMethodSignature *sig = [anInvocation valueForKey:@"_signature"];
const char *returnType = sig.methodReturnType;
if (!strcmp(returnType, "v")) {
    dispatch_barrier_async(_dispatchQueue, ^{
        [anInvocation invokeWithTarget:_container];
    });
}
else {
    dispatch_barrier_sync(_dispatchQueue, ^{
        [anInvocation invokeWithTarget:_container];
    });
}

}
获取调度方法的返回值,如果是void型方法则使用异步调度,如果是getter类型的则使用同步调度,可以略微的提升性能。

你可以通过继承等方法为不同类型的container指定不同的调度规则以确保在逻辑正常的情况下拥有最高的性能。

你可能感兴趣的:(iOS如何实现一个线程安全的 NSMutableArray?)