iOS细节知识收录iOS开发基础知识

iOS-线程安全NSMutableArray

2018-04-15  本文已影响85人  路飞_Luck
数组线程安全的思考

NSMutableArray是线程不安全的,当有多个线程同时对数组进行操作的时候可能导致崩溃或数据错误,下面是对线程安全的几个思路.如有错误,欢迎指正.


数组线程安全的实现

下面咱们来看一下NSMutableArray线程安全的实现

在 Cocoa 中有一种奇葩的类存在 Class Clusters。面向对象的编程告诉我们:“类可以继承,子类具有父类的方法”。而 Cocoa 中的 Class Clusters 虽然平时表现的像普通类一样,但子类却没法继承父类的方法。 NSMutableArray就是这样的存在。为什么会这样呢?因为 Class Clusters 内部其实是由多个私有的类和方法组成。虽然它有这样的弊端,但是好处还是不言而喻的。例如,NSNumber 其实也是这种类,这样一个类可以把各种不同的原始类型封装到一个类下面,提供统一的接口。这正设计模式中的抽象工厂模式。

查看Apple的文档,要继承这样的类需要必须实现其primitive methods方法,实现了这些方法,其它方法便都能通过这些方法组合而成。比如

需要继承NSMutableArray就需要实现它的以下primitive methods:

- (void)addObject:(id)anObject;
- (void)insertObject:(id)anObject atIndex:(NSUInteger)index;
- (void)removeLastObject;
- (void)removeObjectAtIndex:(NSUInteger)index;
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject;

NSArray的primitive methods:

- (NSUInteger)count;
- (id)objectAtIndex:(NSUInteger)index;

#import "BGSafeMutableArray.h"

@interface BGSafeMutableArray() {
    CFMutableArrayRef _array;
}
@end

初始化方法

- (id)init {
    self = [super init];
    if (self) {
        _array = CFArrayCreateMutable(kCFAllocatorDefault, 10,  &kCFTypeArrayCallBacks);
    }
    return self;
}

几个重要方法

// 获取可变数组数量
- (NSUInteger)count {
    __block NSUInteger result;
    dispatch_sync(self.syncQueue, ^{
        result = CFArrayGetCount(_array);
    });
    return result;
}

// 获取第N个位置的对象
- (id)objectAtIndex:(NSUInteger)index {
    __block id result;
    dispatch_sync(self.syncQueue, ^{
        NSUInteger count = CFArrayGetCount(_array);
        result = index < count ? CFArrayGetValueAtIndex(_array, index) : nil;
    });
    return result;
}

// 插入对象至指定位置
- (void)insertObject:(id)anObject atIndex:(NSUInteger)index {
    __block NSUInteger blockIndex = index;
    dispatch_barrier_async(self.syncQueue, ^{
        if (!anObject) {
            return;
        }
        
        NSUInteger count = CFArrayGetCount(_array);
        blockIndex = blockIndex > count ? count : blockIndex;
        
        CFArrayInsertValueAtIndex(_array, index, (__bridge const void *)anObject);
    });
}

// 删除指定位置上的对象
- (void)removeObjectAtIndex:(NSUInteger)index {
    dispatch_barrier_async(self.syncQueue, ^{
        NSUInteger count = CFArrayGetCount(_array);
        if (index < count) {
            CFArrayRemoveValueAtIndex(_array, index);
        }
    });
}

// 添加对象
- (void)addObject:(id)anObject {
    dispatch_barrier_async(self.syncQueue, ^{
        if (!anObject) {
            return;
        }
        CFArrayAppendValue(_array, (__bridge const void *)anObject);
    });
}

// 删除最后一个对象
- (void)removeLastObject {
    dispatch_barrier_async(self.syncQueue, ^{
        NSUInteger count = CFArrayGetCount(_array);
        if (count > 0) {
            CFArrayRemoveValueAtIndex(_array, count-1);
        }
    });
}

// 替换指定位置的对象
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject {
    dispatch_barrier_async(self.syncQueue, ^{
        if (!anObject) {
            return;
        }
        
        NSUInteger count = CFArrayGetCount(_array);
        if (index >= count) {
            return;
        }
        
        CFArraySetValueAtIndex(_array, index, (__bridge const void*)anObject);
    });
}

两个可选方法实现

// 移除所有对象
- (void)removeAllObjects {
    dispatch_barrier_async(self.syncQueue, ^{
        CFArrayRemoveAllValues(_array);
    });
}

// 获取某一个对象的索引位置
- (NSUInteger)indexOfObject:(id)anObject {
    if (!anObject) {
        return NSNotFound;
    }
    
    __block NSUInteger result;
    dispatch_sync(self.syncQueue, ^{
        NSUInteger count = CFArrayGetCount(_array);
        result = CFArrayGetFirstIndexOfValue(_array, CFRangeMake(0, count), (__bridge const void *)(anObject));
    });
    return result;
}

私有属性懒加载

- (dispatch_queue_t)syncQueue {
    static dispatch_queue_t queue = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        queue = dispatch_queue_create("com.kong.NSKSafeMutableArray", DISPATCH_QUEUE_CONCURRENT);
    });
    return queue;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    
    BGSafeMutableArray *safeArr = [[BGSafeMutableArray alloc] init];
    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    for ( int i = 0; i < 10000; i ++) {
        dispatch_async(queue, ^{
            NSLog(@"添加第%d个",i);
            [safeArr addObject:[NSString stringWithFormat:@"%d",i]];
        });
        
        dispatch_async(queue, ^{
            NSLog(@"删除第%d个",i);
            [safeArr removeObjectAtIndex:i];
        });
    }
}

本文参考关于NSMutableArray线程安全的思考和实现非常感谢该作者

上一篇下一篇

猜你喜欢

热点阅读