这周最大的收获是稍稍通透了 多线程安全字典的重要性。 
诱因是,发现了有字典坏地址错误 
 
 
果断以为是 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

iOS 多线程安全 与 可变字典的更多相关文章

  1. iOS 多线程安全 与可变数组

    完全来自于iOS 多线程安全与可变字典 的学习 基本相同,举一反三 直接上样例代码 是我参照网上,根据当前业务需求改的. 其实好多人在这里喜欢用类别处理.我个人觉得用类别 极其容易和普通方法混淆,所以 ...

  2. NSDictionary字典创建,获取,遍历,可变字典的删除 - iOS

    字典是以键值对的形式来存储数据 key value 1 NSDictionary 字典 1.1 创建字典,不可变的 NSDictionary * dic = [NSDictionary diction ...

  3. iOS多线程开发之GCD(中篇)

    前文回顾: 上篇博客讲到GCD的实现是由队列和任务两部分组成,其中获取队列的方式有两种,第一种是通过GCD的API的dispatch_queue_create函数生成Dispatch Queue:第二 ...

  4. iOS多线程编程指南

    iOS多线程编程指南(拓展篇)(1) 一.Cocoa 在Cocoa上面使用多线程的指南包括以下这些: (1)不可改变的对象一般是线程安全的.一旦你创建了它们,你可以把这些对象在线程间安全的传递.另一方 ...

  5. iOS 多线程的使用

    iOS 多线程 先看一篇阮一峰写关于进程和线程的文章,快速了解线程的相关概念. 随着现在计算机硬件的发展,多核心.高频率的cpu越来越普及,为了充分发挥cpu的性能,在不通的环境下实现cpu的利用最大 ...

  6. iOS 多线程 GCD part3:API

    https://www.jianshu.com/p/072111f5889d 2017.03.05 22:54* 字数 1667 阅读 88评论 0喜欢 1 0. 预备知识 GCD对时间的描述有些新奇 ...

  7. iOS多线程开发之GCD(中级篇)

    前文回顾: 上篇博客讲到GCD的实现是由队列和任务两部分组成,其中获取队列的方式有两种,第一种是通过GCD的API的dispatch_queue_create函数生成Dispatch Queue:第二 ...

  8. iOS多线程主题

    下面是:2个并发进程.和2个并发线程的示意图: 下面介绍三种多线程技术(Thread.Cocoa Operation.Grand Central Dispatch): 1.最轻量级Thread(需要自 ...

  9. iOS多线程技术方案

    iOS多线程技术方案 目录 一.多线程简介 1.多线程的由来 2.耗时操作的模拟试验 3.进程和线程 4.多线程的概念及原理 5.多线程的优缺点和一个Tip 6.主线程 7.技术方案 二.Pthrea ...

随机推荐

  1. Docker 和一个正常的虚拟机有何区别?

    问: 我多次重读Docker.io文档,希望搞明白Docker.io和一个完全的虚拟机的区别.Docker是如何做到提供一个完整的文件系统,独立的网络环境等等这些功能,同时还没有如此庞大? 为什么部署 ...

  2. 第一百五十八节,封装库--JavaScript,ajax说明

    封装库--JavaScript,ajax说明 封装库ajax()方法,ajax通讯方法,跨页面向动态页面发送或获取数据 /** ajax()方法,ajax通讯方法,跨页面向动态页面发送或获取数据 * ...

  3. highCharts+Struts2生成柱状图

    这篇文章主要结合Struts2+json+Highcharts实现动态数据的显示.为了节省时间,就不写数据库了.在action中用一个集合来模拟从数据库取到的数据.模拟数据为三个学生在不同时间成绩的变 ...

  4. windows下解决mysql忘记password

    windows下解决mysql忘记password   mysql有时候忘记password了怎么办?我给出案例和说明!一下就攻克了!    Windows下的实际操作例如以下    1.关闭正在执行 ...

  5. ZeroMQ一个更小、更快、更简单的智能传输层协议

    这个githube上的教程是非常好的,是个中文翻译,大家直接学这个就行 https://github.com/anjuke/zguide-cn/tree/master/bin 原文地址: https: ...

  6. boost-tokenizer分词库学习

    boost-tokenizer学习 tokenizer库是一个专门用于分词(token)的字符串处理库;可以使用简单易用的方法把一个字符串分解成若干个单词;tokenizerl类是该库的核心,它以容器 ...

  7. Eclipse用法和技巧

    http://blog.csdn.net/ts1122/article/category/1385337/2

  8. [MySQL] 变量(参数)的查看和设置 [转]

    [MySQL] 变量(参数)的查看和设置 类似于Oracle的参数文件,MySQL的选项文件(如my.cnf)用于配置MySQL服务器,但和Oracle叫法不一样,在MySQL里, 官方叫变量(Var ...

  9. SQL Server系统函数简介[转]

    一.字符转换函数1.ASCII()返回字符表达式最左端字符的ASCII 码值.在ASCII()函数中,纯数字的字符串可不用‘’括起来,但含其它字符的字符串必须用‘’括起来使用,否则会出错.2.CHAR ...

  10. 【BZOJ3931】[CQOI2015]网络吞吐量 最大流

    [BZOJ3931][CQOI2015]网络吞吐量 Description 路由是指通过计算机网络把信息从源地址传输到目的地址的活动,也是计算机网络设计中的重点和难点.网络中实现路由转发的硬件设备称为 ...