iOS 多线程安全 与 可变字典

这周最大的收获是稍稍通透了 多线程安全字典的重要性。 
诱因是,发现了有字典坏地址错误 
 
 
果断以为是 value 或者 key 是可能出现了空值,补充了潜在的判断,虽然有的位置已经预判断的,但是真正赋值的时候并没判断呀,补充上了。
这种问题线下时候,我们基本0复现,所以迭代一个版本用户检验的时候还是报这个错误,不是那种坑坑一直报错势不可挡的bug,但是是很有生命力的bug,不多 还能定位到具体的一行代码。
 
这就让我很困惑了,怎么回事?? key,value 都保证是合法的了,怎么还是会报错,难道还存在字典也会不合法?
当我把我的疑惑在技术群里抛出,马上就能看出
 
哪些是酱油水平:“value key你判断了吗 blabla的”。 内心:我代码都粘出去了,为什么还问这个
哪些就是乱支招:“ runtime 进行方法交换呀” 。       内心:我是想解决问题,不是故意屏蔽问题呀,不是你们的项目是不是通通想搞坏的节奏。
哪些是牛逼指点:“数据源放在串行队列,可以保证没问题 ” 内心:竟然看出了,我这个字典保存的对象是网络请求对象。果真透过现象看本质的人。
 
我也是马上反应过来,这个错误的原因是 多个请求冲突了导致坏地址,需要通过串行队列保证 每次只有一个value key 加入字典 就不会发生野指针。
我也是把我被点得通透的想法问了一下对方,对方也给了肯定的回答。
 
于是我进一步整理了需要处理的逻辑:
我需要有一个串行请求队列,然后保证在加入字典和移除字典时候 在同一个队列,保证线程的一致。。。
并且迅速查找网上相关资源,如参考1中 中第5和第6部分,
正想根据思路写着,那个小哥哥给我发了一份多线程安全字典文件。
 
正对着我当前理解的情况,自己写可能得测试调试,这份有份量的文件值得一提的是,小哥哥说是来自于阿里。。。get
 
以上的工作还是不能够保证字典是线程安全的。。。怎讲?
因为这个字典保存的对象是网络请求对象,除了当前我们设置的串行队列里面持有,网络请求的串行队列里面也会持有这个对象啊,这就是当前问题:资源竞争造成的死锁,野指针也见怪不怪了。
 
所以还要在字典写入和删除的操作上加锁,保证当前独占资源。
这个加锁的逻辑 学习 参考2
 
 我自己是这么用的 安全锁:
- (void)setOperationDicValue:(id)operation forKey:(NSString *)key
{
    NSCondition *mylock = [[NSCondition alloc]init];//创建锁对象
    [mylock lock];//创建锁对象
    [self.operationDict setObject:operation forKey:key];//对共享抢占资源进行操作的代码
    [mylock unlock];//操作完数据,马上释放锁,给其他的线程调用操作
}

- (void)removeOperationObjectForKey:(NSString *)key
{
    NSCondition *mylock = [[NSCondition alloc]init];
    [mylock lock];
    [self.operationDict removeObjectForKey:key];
    [mylock unlock];
}

- (void)removeAllOperationObjects
{
    NSCondition *mylock = [[NSCondition alloc]init];
    [mylock lock];
    [self.operationDict removeAllObjects];
    [mylock unlock];
}

- (id)getOperationObjectValueForKey:(NSString *)key
{
   return [self.operationDict objectForKey:key];
}

关键怎么让线程不安全的字典变安全:(好东西要分享呀)

//
//  SyncMutableDictionary.h
//  banggood
//
//  Created by Artillery on 2017/10/16.
//  Copyright © 2017年 banggood. All rights reserved.
//

#import <Foundation/Foundation.h>
/*
 多线程下的安全字典 来自阿里
 */
@interface SyncMutableDictionary : NSObject

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

- (nullable id)valueForKey:(_Nonnull id)aKey;

- (NSArray * _Nonnull)allKeys;

- (void)setObject:(nullable id)anObject forKey:(_Nonnull id <NSCopying>)aKey;

- (void)removeObjectForKey:(_Nonnull id)aKey;

- (void)removeAllObjects;

- (NSMutableDictionary *_Nonnull)getDictionary;

@end

//
//  SyncMutableDictionary.m
//  banggood
//
//  Created by Artillery on 2017/10/16.
//  Copyright © 2017年 banggood. All rights reserved.
//

#import "SyncMutableDictionary.h"

@interface SyncMutableDictionary ()

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

@end

@implementation SyncMutableDictionary

- (instancetype)init {
    if (self = [super init]) {
        _dictionary = [NSMutableDictionary new];
        _dispatchQueue = dispatch_queue_create("com.banggood.banggoodSycmutableDictionary", DISPATCH_QUEUE_SERIAL);
    }
    return self;
}

- (NSArray * _Nonnull)allKeys{
    __block NSArray *allKeys = [NSArray array];
    dispatch_sync(_dispatchQueue, ^{
        allKeys = [_dictionary allKeys];
    });
    return allKeys;
}

- (nullable id)objectForKey:(_Nonnull id)aKey{
    __block id returnObject = nil;
    if(!aKey) return returnObject;
    dispatch_sync(_dispatchQueue, ^{
        returnObject = _dictionary[aKey];
    });
    return returnObject;
}

- (void)setValue:(nullable id)value forKey:(NSString *)key {
    if(!key) return;
    dispatch_barrier_async(_dispatchQueue, ^{
        [_dictionary setValue:value forKey:key];
    });
}

- (nullable id)valueForKey:(_Nonnull id)aKey{
    __block id returnObject = nil;
    dispatch_sync(_dispatchQueue, ^{
        returnObject = [_dictionary valueForKey:aKey];
    });
    return returnObject;
}

- (void)setObject:(nullable id)anObject forKey:(_Nonnull id <NSCopying>)aKey{
    dispatch_barrier_async(_dispatchQueue, ^{
        if (anObject == nil) return;
        self.dictionary[aKey] = anObject;
    });
}

- (void)removeObjectForKey:(_Nonnull id)aKey{
    if(!aKey) return;
    dispatch_sync(_dispatchQueue, ^{
        [_dictionary removeObjectForKey:aKey];
    });
}

- (void)removeAllObjects {
    dispatch_sync(_dispatchQueue, ^{
        [_dictionary removeAllObjects];
    });
}

- (NSMutableDictionary *)getDictionary {
    __block NSMutableDictionary *temp;
    dispatch_sync(_dispatchQueue, ^{
        temp = _dictionary;
    });
    return temp;
}


-(NSString *)description{
    return [NSString stringWithFormat:@"%@",self.dictionary];
}

@end

 

至此总结:

可变字典,(同理可变数组等)是线程不安全的,以后尽量减少在多线程的情况下 处理数据源的情况。

如像我这次这样需要使用的话,处理成多线程安全字典和加安全锁。 

参考 
1.https://www.cnblogs.com/alunchen/p/5607821.html
2.https://www.cnblogs.com/XYQ-208910/p/4857470.html

posted on 2018-05-24 16:32  ACM_Someone like you  阅读(2525)  评论(0编辑  收藏  举报

导航