一、容器类多线程读写的问题
我们看苹果的官方文档会发现 NSMutableArray 和NSMutableDictionary 都不是线程安全的,这就带来一个问题,主线程我们多次操作 都没有问题,但是多线程下短时间内有大量的读写操作的时候是否会引起数据的错乱?只要简单测试下 答案就会不言而喻,NSMutableArray在多线程下操作很容易引起数组越界二导致crash。
以NSMutableArray 为例 可以简单模仿下 多线程对数组的读写操作
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]];
});
}
这样直接会crash
怎样解决呢?有人说很简单 加个 @synchronized 就可以解决
for (int i = 0 ; i < 1000; i++) {
dispatch_async(quene, ^{
@synchronized (originArray) {
[originArray addObject:[NSString stringWithFormat:@"item%ld", i]];
}
});
}
其实同步或者加锁的手段中 synchronized 的效率是最低的
二、读写加锁的解决方案
关于 NSMutableArray 的读写 安全高效的做法是这样的在读的时候我们使用GCD同步机制,写的时候使用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;
}
三、对于SafeNSMutableArray的封装
我们可以新建一个继承自 NSObject的类,然后加一个NSMutableArray的属性,然后将NSMutableArray 的一些 增删改查方法在新建立的类中重写
代码如下
.h文件
#import
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
上边的方法 基本涵盖了数组的基本操作
其实对于 NSMutableDictionary 道理是一样的,不再赘述,直接贴下代码
.h文件
#import
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
封装很简单,使用起来和原生的NSMutableArray 和 NSMutableDictionary 的方法也都差不多,如果需要其他的方法 还可以自行添加。