“线程安全”:即多条线程可以安全的访问同一数据,不会引起崩溃和数据错误
那为什么会造成这一问题呢?那就要说到咱们的多线程了。多线程并发的本意就是充分使用系统资源去提升性能。但是当我们使用多线程去操作一个对象的属性(对象Person中有一个age属性)时,由于线程和线程之间默认是没有进行通信的,这就导致,A线程去操作属性age“++”,但是此时B线程并不知道,然后B线程就去操作属性age“--”,然后等到A线程再去使用age“++”后的数据进行操作,发现数据已经出错了
。这就是多线程并发带来的问题
但其实NSMutableDictionary
和NSMutableArray
并没有做到这一点,它们在iOS开发当中都不是线程安全的,在处理多线程下操作NSMutableDictionary
和NSMutableArray
,都需要我们自己去设计线程安全的保护机制,让我们在进行数据读写操作时,保证线程安全,也让我们的代码可以更健壮
首先我们就要知道,设计线程安全的保护机制有几种?该用什么?效率怎么样?
- 在iOS当前技术下,处理线程安全的机制有两种:同步锁、派发队列。
- 派发队列中包含了两种方式:同步串行队列,同步并发队列+GCD的栅栏块(
dispatch_barrier_async/dispatch_barrier_sync
)结合
不同方式对比:
- 锁:主要是同步锁,会让每一个线程都进行等待,效率会比较低,极端情况还可能造成死锁的情况
- 同步串行队列:将数据读取操作及写入操作都放在了同一个串行队列当中,这样顺序执行读写操作,可以保证数据安全
- 同步并发队列+GCD的栅栏块:同步并发保证了
目前我采用的方式都是同步方式,如果说在操作数据的任务中比较耗时建议使用异步方式(不是特别耗时的任务,同步效率是要高于异步的)
通过以上思路,相信代码已经不难写出,建立一个安全dictionary类,命名为:EGSafeMutableDict
代码如下:
EGSafeMutableDict.h
文件
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface EGSafeMutableDict : NSMutableDictionary
//单例
+(instancetype)shareSafeMutableDict;
//NSMutableDictionary中key值为KeyType类型,除了nil、可变对象外,均可以做key,这里设置为id类型
- (nullable id)objectForKey:(_Nonnull id)aKey;
- (void)setObject:(nullable id)anObject forKey:(_Nonnull id <NSCopying>)aKey;
- (void)removeObjectForKey:(_Nonnull id)aKey;
- (NSMutableDictionary *_Nonnull)getDictionary;
@end
NS_ASSUME_NONNULL_END
EGSafeMutableDict.m
文件
#import "EGSafeMutableDict.h"
@interface EGSafeMutableDict()
@property(nonatomic, strong) NSMutableDictionary *dictionary;
@property(nonatomic, strong) dispatch_queue_t dispatchQueue;
@end
@implementation EGSafeMutableDict
//单例
+(instancetype)shareSafeMutableDict{
static EGSafeMutableDict *_safeMutableDict = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_safeMutableDict = [[super allocWithZone:NULL]init];
});
return _safeMutableDict;
}
+(instancetype)allocWithZone:(struct _NSZone *)zone{
return [EGSafeMutableDict shareSafeMutableDict];
}
-(id)copyWithZone:(struct _NSZone *)zone{
return [EGSafeMutableDict shareSafeMutableDict];
}
- (instancetype)init
{
self = [super init];
if (self) {
_dictionary = [[NSMutableDictionary alloc]init];
_dispatchQueue = dispatch_queue_create("com.echo.equal.20200916safequeue", DISPATCH_QUEUE_CONCURRENT);
}
return self;
}
- (nullable id)objectForKey:(_Nonnull id)aKey{
__block id object = nil;
if (!aKey) return object;
dispatch_sync(_dispatchQueue, ^{
object = _dictionary[aKey];
});
return object;
}
- (void)setObject:(nullable id)anObject forKey:(_Nonnull id <NSCopying>)aKey{
if (!aKey) return;
dispatch_barrier_async(_dispatchQueue, ^{
[self->_dictionary setObject:anObject forKey:aKey];
});
}
- (void)removeObjectForKey:(_Nonnull id)aKey{
if(!aKey) return;
dispatch_sync(_dispatchQueue, ^{
[_dictionary removeObjectForKey:aKey];
});
}
- (NSMutableDictionary *_Nonnull)getDictionary{
__block NSMutableDictionary *_safeDict;
dispatch_sync(_dispatchQueue, ^{
_safeDict = _dictionary;
});
return _safeDict;
}
@end
暂时先写了两三个方法,后续会完善该类,并集成到EGuoLibs库中去,并增加NSMutableArray
的安全可变对象的方法编写。
如果上述有不对的或者不完善的,敬请指出,共同进步!!!
<启文行书>
网友评论