关于NSMutableArray线程安全的思考和实现

数组线程安全的思考

NSMutableArray是线程不安全的,当有多个线程同时对数组进行操作的时候可能导致崩溃或数据错误,下面是我对线程安全的几个思路,希望由此能给你带来一些思路,如果有错误的地方还希望大家能够指出
1.对数组的读写都加锁,虽然数组是线程安全了,但失去了多线程的优势

2.然后又想可以只对写操作加锁然后定义一个全局变量来表示现在有没有写操作,如果有写操作就等写完了在读,那么问题来了如果一个线程先读取数据紧接着一个线程对数组写的操作,读的时候还没有加锁同样会导致崩溃或数据错误,这个方案pass掉

3.第三种方案说之前先介绍一下dispatch_barrier_async,dispatch_barrier_async 追加到 queue 中后,会等待 queue 中的任务都结束后,再执行 dispatch_barrier_async 的任务,等 dispatch_barrier_async 的任务结束后,才恢复任务执行, 用dispatch_async和dispatch_barrier_async结合保证NSMutableArray的线程安全,用dispatch_async读和dispatch_barrier_async写(add,remove,replace),当有任务在读的时候写操作会等到所有的读操作都结束了才会写,同样当有写任务时,读任务会等写操作完了才会读,既保证了线程安全又发挥了多线程的优势,但还是有个不足,当我们重写读的方法时dispatch_async是另开辟线程去执行的而且是立马返回的,所以我们不能拿到执行结果,需要去另写一个方法来返回读的结果,但是我们又不想改变调用者的习惯于是又想到了一下方案

4.用dispatch_sync和dispatch_barrier_async结合保证NSMutableArray的线程安全,dispatch_sync是在当前线程上执行不会另开辟新的线程,当线程返回的时候就可以拿到读取的结果,我认为这个方案是最完美的选择,既保证的线程安全有发挥了多线程的优势还不用另写方法返回结果,完美~

数组线程安全的实现

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

  • 1 继承 NSMutableArray创建NSKSafeMutableArray在这个地方遇到了一些坑通过查阅文档发现问题所在:

在 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;
  • 2 NSKSafeMutableArray.h 的实现如下
#import "NSKSafeMutableArray.h"


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

@implementation NSKSafeMutableArray

- (id)init
{
    return [self initWithCapacity:10];
}

- (id)initWithCapacity:(NSUInteger)numItems
{
    self = [super init];
    if (self)
    {
        _array = CFArrayCreateMutable(kCFAllocatorDefault, numItems,  &kCFTypeArrayCallBacks);
    }
    return self;
}
- (NSUInteger)count {

    __block NSUInteger result;
    dispatch_sync(self.syncQueue, ^{
        result = CFArrayGetCount(_array);
    });
    return result;
}

- (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);
        if (blockindex > count) {
            blockindex = count;
        }
        CFArrayInsertValueAtIndex(_array, index, (__bridge const void *)anObject);

    });

}

- (void)removeObjectAtIndex:(NSUInteger)index
{

    dispatch_barrier_async(self.syncQueue, ^{

        NSUInteger count = CFArrayGetCount(_array);
        NSLog(@"count:%lu,index:%lu",(unsigned long)count,(unsigned long)index);
        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);
        CFArraySetValueAtIndex(_array, index, (__bridge const void*)anObject);
    });
}

#pragma mark Optional

- (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;


    return result;
}
#pragma mark - Private
- (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;

}
@end
  • 3 调用
- (void)viewDidLoad {
    [super viewDidLoad];
    NSKSafeMutableArray *safeArr = [[NSKSafeMutableArray alloc] init];

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    for ( int i = 0; i < 5; i ++) {
        dispatch_async(queue, ^{

            NSLog(@"添加第%d个",i);
            [safeArr addObject:[NSString stringWithFormat:@"%d",i]];

        });

        dispatch_async(queue, ^{

            NSLog(@"删除第%d个",i);
            [safeArr removeObjectAtIndex:i];

        });
    }

    // Do any additional setup after loading the view, typically from a nib.
}

源代码请点击

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
NSMutableArray是Objective-C中的可变数组类,常用于存储和管理一组有序的对象。以下是NSMutableArray的常见用法: 1. 创建NSMutableArray对象: ```objective-c NSMutableArray *array = [NSMutableArray array]; // 空数组 NSMutableArray *array = [NSMutableArray arrayWithObjects:@"obj1", @"obj2", nil]; // 初始化含有对象的数组 ``` 2. 添加和删除对象: ```objective-c [array addObject:@"obj3"]; // 在数组末尾添加对象 [array insertObject:@"obj4" atIndex:2]; // 在指定索引位置插入对象 [array removeObject:@"obj2"]; // 移除指定对象 [array removeObjectAtIndex:0]; // 移除指定索引位置的对象 ``` 3. 替换和交换对象: ```objective-c [array replaceObjectAtIndex:1 withObject:@"newObj"]; // 替换指定索引位置的对象 [array exchangeObjectAtIndex:0 withObjectAtIndex:2]; // 交换两个索引位置的对象 ``` 4. 获取数组信息: ```objective-c NSUInteger count = [array count]; // 获取数组中对象的数量 id obj = [array objectAtIndex:2]; // 获取指定索引位置的对象 NSInteger index = [array indexOfObject:@"obj3"]; // 获取指定对象的索引位置 BOOL containsObj = [array containsObject:@"obj2"]; // 判断数组是否包含指定对象 ``` 5. 遍历数组: ```objective-c for (id obj in array) { // 遍历数组中的对象 } [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { // 使用block遍历数组中的对象,可以获取对象、索引和停止遍历的标志 }]; ``` 6. 数组排序: ```objective-c [array sortUsingComparator:^NSComparisonResult(id obj1, id obj2) { // 自定义的比较方法,用于数组排序 }]; ``` 这只是NSMutableArray的一些常见用法,还有其他更多的方法和功能可供使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值