• 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
  • 相关阅读:
    树莓派4 (1)一键配置
    Android编码学习之Fragment
    android编码学习
    自动化中app支持schema跳转
    jenkins持续集成
    测试环境运维文章集锦
    HTML5 data-* 自定义属性
    了解一下JavaScript的未来——ECMAScript5
    ECMAScript5的其它新特性
    ECMAScript5 Array新增方法
  • 原文地址:https://www.cnblogs.com/someonelikeyou/p/9083506.html
Copyright © 2020-2023  润新知