NSMutableArray 线程安全方案

线程安全的NSMutableArray

NSMutableArray本身是线程不安全的。多线程访问NSMutableArray 会出现异常和Crash

一、不能使用atomic 修饰属性

原因:atomic 的内存管理语义是原子性的,仅保证了属性的setter和getter方法是原子性的,是线程安全的,但是属性的其他方法,不能保证线程安全。同时atomic 效率低,编程时慎用。

二、打造线程安全的NSMutableArray

1、读写数组时加锁
2、读写数组放到串行队列中。
3、使用并发队列,结合GCD的拦珊块(barrier)来实现线程同步。

第三种实现方式:

@interface QSThreadSafeMutableArray : NSMutableArray

@end

//QSThreadSafeMutableArray.m
#import "QSThreadSafeMutableArray.h"
@interface QSThreadSafeMutableArray()

@property (nonatomic, strong) dispatch_queue_t syncQueue;
@property (nonatomic, strong) NSMutableArray* array;

@end

@implementation QSThreadSafeMutableArray

#pragma mark - init 方法
- (instancetype)initCommon{

    self = [super init];
    if (self) {
        //%p 以16进制的形式输出内存地址,附加前缀0x
        NSString* uuid = [NSString stringWithFormat:@"com.jzp.array_%p", self];
        //注意:_syncQueue是并行队列
        _syncQueue = dispatch_queue_create([uuid UTF8String], DISPATCH_QUEUE_CONCURRENT);
    }
    return self;
}

- (instancetype)init{

    self = [self initCommon];
    if (self) {
        _array = [NSMutableArray array];
    }
    return self;
}

//其他init方法略

#pragma mark - 数据操作方法 (凡涉及更改数组中元素的操作,使用异步派发+栅栏块;读取数据使用 同步派发+并行队列)
- (NSUInteger)count{

    __block NSUInteger count;
    dispatch_sync(_syncQueue, ^{
        count = _array.count;
    });
    return count;
}

- (id)objectAtIndex:(NSUInteger)index{

    __block id obj;
    dispatch_sync(_syncQueue, ^{
        if (index < [_array count]) {
            obj = _array[index];
        }
    });
    return obj;
}

- (NSEnumerator *)objectEnumerator{

    __block NSEnumerator *enu;
    dispatch_sync(_syncQueue, ^{
        enu = [_array objectEnumerator];
    });
    return enu;
}

- (void)insertObject:(id)anObject atIndex:(NSUInteger)index{

    dispatch_barrier_async(_syncQueue, ^{
        if (anObject && index < [_array count]) {
            [_array insertObject:anObject atIndex:index];
        }
    });
}

- (void)addObject:(id)anObject{

    dispatch_barrier_async(_syncQueue, ^{
        if(anObject){
           [_array addObject:anObject];
        }
    });
}

- (void)removeObjectAtIndex:(NSUInteger)index{

    dispatch_barrier_async(_syncQueue, ^{

        if (index < [_array count]) {
            [_array removeObjectAtIndex:index];
        }
    });
}

- (void)removeLastObject{

    dispatch_barrier_async(_syncQueue, ^{
        [_array removeLastObject];
    });
}

- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject{

    dispatch_barrier_async(_syncQueue, ^{
        if (anObject && index < [_array count]) {
            [_array replaceObjectAtIndex:index withObject:anObject];
        }
    });
}

- (NSUInteger)indexOfObject:(id)anObject{

    __block NSUInteger index = NSNotFound;
    dispatch_sync(_syncQueue, ^{
        for (int i = 0; i < [_array count]; i ++) {
            if ([_array objectAtIndex:i] == anObject) {
                index = i;
                break;
            }
        }
    });
    return index;
}

- (void)dealloc{

    if (_syncQueue) {
        _syncQueue = NULL;
    }
}
@end

参考:
NSMutableArray使用中忽视的问题
dispatch_async与dispatch_sync区别
GCD有关问题:dispatch_sync(dispatch_get_main_queue(), ^{NSLog(@"Hello ?");}); 死锁的原因
dispatch_barrier_sync 和dispatch_barrier_async的区别
iOS多线程-GCD之串行队列和并行队列

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 本文用来介绍 iOS 多线程中 GCD 的相关知识以及使用方法。这大概是史上最详细、清晰的关于 GCD 的详细讲...
    花花世界的孤独行者阅读 530评论 0 1
  • 文章目录GCD简介任务和队列GCD的使用步骤队列的创建方法任务的创建方法GCD的基本使用并行队列 + 同步执行并行...
    lusen_b阅读 258评论 0 1
  • 目录一、GCD的两对儿主要概念及它们的六种组合  1、dispatch_sync和dispatch_async  ...
    意一ineyee阅读 1,473评论 0 0
  • NSThread 第一种:通过NSThread的对象方法 NSThread *thread = [[NSThrea...
    攻城狮GG阅读 869评论 0 3
  • 本篇博客共分以下几个模块来介绍GCD的相关内容: 多线程相关概念 多线程编程技术的优缺点比较? GCD中的三种队列...
    有梦想的老伯伯阅读 1,042评论 0 4