NSMutableArray和NSMutableDictionary多線程安全讀寫

一、容器類多線程讀寫的問(wèn)題
我們看蘋果的官方文檔會(huì)發(fā)現(xiàn) NSMutableArray 和NSMutableDictionary 都不是線程安全的,這就帶來(lái)一個(gè)問(wèn)題,主線程我們多次操作 都沒(méi)有問(wèn)題,但是多線程下短時(shí)間內(nèi)有大量的讀寫操作的時(shí)候是否會(huì)引起數(shù)據(jù)的錯(cuò)亂?只要簡(jiǎn)單測(cè)試下 答案就會(huì)不言而喻,NSMutableArray在多線程下操作很容易引起數(shù)組越界二導(dǎo)致crash。
以NSMutableArray 為例 可以簡(jiǎn)單模仿下 多線程對(duì)數(shù)組的讀寫操作

dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    NSMutableArray *originArray = [NSMutableArray new];
    for (int i = 0 ; i < 1000; i++) {
        dispatch_async(quene, ^{
            [originArray addObject:[NSString stringWithFormat:@"item%ld", i]];
        });
    }

這樣直接會(huì)crash
怎樣解決呢?有人說(shuō)很簡(jiǎn)單 加個(gè) @synchronized 就可以解決

 for (int i = 0 ; i < 1000; i++) {
        dispatch_async(quene, ^{
            @synchronized (originArray) {
                [originArray addObject:[NSString stringWithFormat:@"item%ld", i]];
            }
        });
    }

其實(shí)同步或者加鎖的手段中 synchronized 的效率是最低的
二、讀寫加鎖的解決方案
關(guān)于 NSMutableArray 的讀寫 安全高效的做法是這樣的在讀的時(shí)候我們使用GCD同步機(jī)制,寫的時(shí)候使用GCD的Barrier

//模仿的寫操作
- (void)addItem:(id)item {
    dispatch_barrier_async(self.readWriteQuene, ^{
        [self.array addObject:item];
    });
}

//模仿的讀操作
- (id)getLastItem {
    __block id item = nil;
    dispatch_sync(self.readWriteQuene, ^{
        NSUInteger size = self.array.count;
        if (size > 0) {
            item = self.array[size - 1];
        }
    });
    return item;
}

三、對(duì)于SafeNSMutableArray的封裝
我們可以新建一個(gè)繼承自 NSObject的類,然后加一個(gè)NSMutableArray的屬性,然后將NSMutableArray 的一些 增刪改查方法在新建立的類中重寫
代碼如下
.h文件

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface NSSafeMutableArray : NSObject

- (void)addObject:(id)anObject;

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

- (void)removeLastObject;

- (void)removeObjectAtIndex:(NSUInteger)index;

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

- (id)objectAtIndex:(NSUInteger)index;

- (nullable id)getFirstObject;

- (nullable id)getLastObject;

@end

NS_ASSUME_NONNULL_END

.m文件

#import "NSSafeMutableArray.h"
@interface NSSafeMutableArray()

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

@end

@implementation NSSafeMutableArray
- (instancetype)init {
   self = [super init];
   if (self) {
       _array = [NSMutableArray array];
       _readWriteQuene = dispatch_queue_create("com.liwb.quene", DISPATCH_QUEUE_CONCURRENT);
   }
   return self;
}

- (void)addObject:(id)anObject {
   dispatch_barrier_async(self.readWriteQuene, ^{
       [self.array addObject:anObject];
   });
}

- (void)insertObject:(id)anObject atIndex:(NSUInteger)index {
   dispatch_barrier_async(self.readWriteQuene, ^{
       [self.array insertObject:anObject atIndex:index];
   });
}

- (void)removeLastObject {
   dispatch_barrier_async(self.readWriteQuene, ^{
       [self.array removeLastObject];
   });
}

- (void)removeObjectAtIndex:(NSUInteger)index {
   dispatch_barrier_async(self.readWriteQuene, ^{
       [self.array removeObjectAtIndex:index];
   });
}

- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject {
   dispatch_barrier_async(self.readWriteQuene, ^{
       [self.array replaceObjectAtIndex:index withObject:anObject];
   });
}

- (id)objectAtIndex:(NSUInteger)index {
   __block id item = nil;
   dispatch_sync(self.readWriteQuene, ^{
       if (index <= self.array.count - 1) {
           item = [self.array objectAtIndex:index];
       }
   });
   return item;
}
- (nullable id)getFirstObject {
   __block id item = nil;
   dispatch_sync(self.readWriteQuene, ^{
       if (self.array.count > 0) {
           item = [self.array objectAtIndex:0];
       }
   });
   return item;
}
- (nullable id)getLastObject {
   __block id item = nil;
   dispatch_sync(self.readWriteQuene, ^{
       NSUInteger size = self.array.count;
       if (size > 0) {
           item = self.array[size - 1];
       }
   });
   return item;
}

@end

上邊的方法 基本涵蓋了數(shù)組的基本操作
其實(shí)對(duì)于 NSMutableDictionary 道理是一樣的,不再贅述,直接貼下代碼
.h文件

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface NSSafeMutableDictionary : NSObject

- (void)removeObjectForKey:(id)aKey;

- (void)setObject:(id)anObject forKey:(id)aKey;

- (nullable id)objectForKey:(id)aKey;

- (NSArray *)allKeys;

- (NSArray *)allValues;

@end

NS_ASSUME_NONNULL_END

.m文件

#import "NSSafeMutableDictionary.h"
@interface NSSafeMutableDictionary ()

@property (nonatomic, strong) NSMutableDictionary *dictionary;
@property (nonatomic, strong) dispatch_queue_t readWriteQuene;

@end

@implementation NSSafeMutableDictionary

- (instancetype)init {
    self = [super init];
    if (self) {
        _dictionary = [NSMutableDictionary dictionary];
        _readWriteQuene = dispatch_queue_create("com.liwb.quene", DISPATCH_QUEUE_CONCURRENT);
    }
    return self;
}

- (void)removeObjectForKey:(id)aKey {
    dispatch_barrier_async(self.readWriteQuene, ^{
        [self.dictionary removeObjectForKey:aKey];
    });
}

- (void)setObject:(id)anObject forKey:(id)aKey {
    dispatch_barrier_async(self.readWriteQuene, ^{
        [self.dictionary setObject:anObject forKey:aKey];
    });
}

- (nullable id)objectForKey:(id)aKey {
    __block id item = nil;
    dispatch_sync(self.readWriteQuene, ^{
        item = [self.dictionary objectForKey:aKey];
    });
    return item;
}

- (NSArray *)allKeys {
    __block NSArray *keys;
    dispatch_sync(self.readWriteQuene, ^{
        keys = [self.dictionary allKeys];
    });
    return keys;
}

- (NSArray *)allValues {
    __block NSArray *values;
    dispatch_sync(self.readWriteQuene, ^{
        values = [self.dictionary allValues];
    });
    return values;
    
}
@end

封裝很簡(jiǎn)單,使用起來(lái)和原生的NSMutableArray 和 NSMutableDictionary 的方法也都差不多,如果需要其他的方法 還可以自行添加。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒(méi)有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,621評(píng)論 1 32
  • 下面是我最近兩年學(xué)習(xí)OC中的一些基礎(chǔ)知識(shí),對(duì)于學(xué)習(xí)OC基礎(chǔ)知識(shí)的人可能有些幫助,拿出來(lái)分享一下,還是那句話不喜勿噴...
    小小趙紙農(nóng)閱讀 2,821評(píng)論 1 7
  • ??一個(gè)任務(wù)通常就是一個(gè)程序,每個(gè)運(yùn)行中的程序就是一個(gè)進(jìn)程。當(dāng)一個(gè)程序運(yùn)行時(shí),內(nèi)部可能包含了多個(gè)順序執(zhí)行流,每個(gè)順...
    OmaiMoon閱讀 1,800評(píng)論 0 12
  • 線程安全是怎么產(chǎn)生的 常見比如線程內(nèi)操作了一個(gè)線程外的非線程安全變量,這個(gè)時(shí)候一定要考慮線程安全和同步。 - (v...
    幽城88閱讀 758評(píng)論 0 0
  • 01 這是力量的懸殊之戰(zhàn),天境與凡人之間,愛意與命運(yùn)之間,勝者得到全部,敗者灰飛煙滅,盡管這不過(guò)是一場(chǎng)游戲,以命運(yùn)...
    1d92a958b904閱讀 1,557評(píng)論 0 6

友情鏈接更多精彩內(nèi)容