前言
在介绍异步读写之前,先介绍一下多线程下崩溃的容易崩溃的原因,以及如何进行**同步读,异步写**
一. 多线程常见崩溃
MRC下:
-(id) getSafeObjectForKey:(NSString *) bindingKey
{
@synchronized(self.dic) {
if(bindingKey){
return [self.dic objectForKey:bindingKey];
}
}
return nil;
}
-(void) setSafeObject:(id) object forKey:(NSString*) bindingKey
{
@synchronized(self.dic) {
if(bindingKey && object){
[self.dic setObject:object forKey:bindingKey];
}
}
}
-(void) testSyncizedDictionary
{
for (int i = 0; i < 1000000; i++) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//多线程写
[self setSafeObject:[NSString stringWithFormat:@"86+111111111 %i", i] forKey:KEY];
});
//主线程读
NSString *result = [self getSafeObjectForKey:KEY];
NSLog(@"get string: %@, length : %lu", result, result.length);
}
}
原因分析:程序还没执行到NSLog那一行的时候,get出来的一个值,在多线程中执行了set方法执行导致key对应的result对释放掉了。此时执行到NSLog,导致了崩溃.
修改方案:
//主线程读
NSString *result = [[self getSafeObjectForKey:KEY] retain];
NSLog(@"get string: %@, length : %lu", result, result.length);
[result release];
在读取的时候retain一下,后续在release,还是会崩溃。因为在retain之前很可能野掉了,此时retain也只是对野掉的的指针
二. 最终修改方案
MRC下
#pragma mark- 用GCD的方式去实现
-(id) getRightObjectForKey:(NSString *) bindingKey
{
if (!bindingKey) {
return nil;
}
__block id result = nil;
dispatch_sync(self.ioQueue, ^{
result = [[_dic objectForKey:bindingKey] retain]; //关键点1
});
return [result autorelease]; //关键点2
}
-(void) setRightObject:(id) object forKey:(NSString*) bindingKey
{
bindingKey = [bindingKey copy];
dispatch_barrier_async(self.ioQueue, ^{
if(bindingKey && object){
[_dic setObject:object forKey:bindingKey];
}
});
}
-(void) testSyncizedDictionaryRight
{
for (int i = 0; i < 1000000; i++) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//多线程写
[self setRightObject:[NSString stringWithFormat:@"86+111111111 %i", i] forKey:KEY];
});
//主线程读
NSString *result = [[self getRightObjectForKey:KEY] retain];
NSLog(@"get string: %@, length : %lu", result, result.length);
[result release];
}
}
请看关键点1和关键点2,如果没有retain和autorelease,还是会崩溃。原因同上。
ARC下:
怎么操作都不崩溃。不过还是建议写法
@property(nonatomic,strong) NSMutableDictionary *dic;
@property(nonatomic,strong) dispatch_queue_t ioQueue;
self.ioQueue = dispatch_queue_create("ioQueue", DISPATCH_QUEUE_CONCURRENT);
self.dic = [NSMutableDictionary dictionaryWithCapacity:1];
#pragma mark- 用GCD的方式去实现
-(id) getRightObjectForKey:(NSString *) bindingKey
{
if (!bindingKey) {
return nil;
}
__block id result = nil;
dispatch_sync(self.ioQueue, ^{
result = [_dic objectForKey:bindingKey]; });
return result;
}
-(void) setRightObject:(id) object forKey:(NSString*) bindingKey
{
bindingKey = [bindingKey copy];
dispatch_barrier_async(self.ioQueue, ^{
if(bindingKey && object){
[_dic setObject:object forKey:bindingKey];
}
});
}
-(void) testSyncizedDictionaryRight
{
for (int i = 0; i < 1000000; i++) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//多线程写
[self setRightObject:[NSString stringWithFormat:@"86+111111111 %i", i] forKey:KEY];
});
//主线程读
NSString *result = [self getRightObjectForKey:KEY];
NSLog(@"get string: %@, length : %lu", result, result.length);
}
}
三. 异步读写
创建一个串行队列。然后gcd结合锁去执行
@property(nonatomic,strong) dispatch_queue_t asynModelQueue;
self.asynModelQueue = dispatch_queue_create("AsynModelQueue", DISPATCH_QUEUE_SERIAL);
#pragma mark- 用GCD去异步读写
-(void) getAsyncRightObjectForKey:(NSString *) bindingKey block:(void(^)(NSMutableDictionary* modelDicCache)) modelCallBack
{
if (!bindingKey && modelCallBack) {
modelCallBack(nil);
return;
}
__block id result = nil;
dispatch_async(self.asynModelQueue, ^{
@synchronized(self) {//@synchronized是为了多一层保护
result = [_dic objectForKey:bindingKey]; //由于self.bindingInfo重写过,此处不能用self.bindingInfo,否则造成死锁
modelCallBack(result);
}
});
}
-(void) setAsyncRightObject:(id) object forKey:(NSString*) bindingKey
{
bindingKey = [bindingKey copy];
dispatch_barrier_async(self.asynModelQueue, ^{
@synchronized(self) {//@synchronized是为了多一层保护
if(bindingKey && object){
[_dic setObject:object forKey:bindingKey];
}
}
});
}
-(void) testAsyncSyncizedDictionaryRight
{
for (int i = 0; i < 1000000; i++) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//多线程写
[self setRightObject:[NSString stringWithFormat:@"86+111111111 %i", i] forKey:KEY];
});
//主线程读
NSString *result = [self getRightObjectForKey:KEY];
NSLog(@"get string: %@, length : %lu", result, result.length);
}
}