一、容器類多線程讀寫的問(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 的方法也都差不多,如果需要其他的方法 還可以自行添加。