这是我参与8月更文挑战的第23天,活动详情查看: 8月更文挑战
我们接着上一篇文章iOS底层分析-锁(一)继续分析锁;
synchronized中SyncData的结构
我们先来看一下SyncData
输一个什么样的数据结构:
SyncData
是一个结构体类型的数据;我们注意到其结构体内部有一个SyncData* nextData
的成员变量,看到这里,我们会下意识的想起链表。因为只有有一个nextData
,所以它有可能是一个单向链表
;DisguisedPtr<objc_object> object;
我们在学习关联对象
的时候看到过这样的代码,是一个ptr
类型的封装;threadCount
有多少线程正在使用这个block
;recursive_mutex_t mutex;
是一个递归锁
,只能递归使用,但是不能多线程的递归;
知道了SyncData
的数据结构之后,接下来我们分析id2data
方法;
synchronized的数据结构
tls线程相关知识补充
在讲id2data
方法之前,我们需要补充一下tls
的相关知识;
线程局部存储Thread Local Storage, TLS
:是操作系统为线程单独提供的私有空间,通常只有有限的容量。Linex
系统下通常通过pthread
库中的一下几个函数进行操作:
- pthread_key_create()
- pthread_getspecific()
- pthread_setspecific()
- pthread_key_delete()
id2data方法分析
我们点击进入id2data
方法的实现,这个方法相当复杂,那么我们关闭一下复杂的操作,从整体上来看一下这个方法:
我们可以发现,id2data
方法主要进行了以下操作:
data
存储- 开辟内存空间,给
SyncData
类型的result
赋值; lock()
和unlock()
是保证方法体内部进行内存开辟时的线程安全;跟外部的锁没有关系;- 最终返回一个
result
;
接下来,我们进行详细的分析:
spinlock_t *lockp = &LOCK_FOR_OBJ(object);
通过宏#define LOCK_FOR_OBJ(obj) sDataLists[obj].lock
和object
获取了一把锁;SyncData **listp = &LIST_FOR_OBJ(object);
通过宏#define LIST_FOR_OBJ(obj) sDataLists[obj].data
和object
获取了一个SyncData
;
那么我们不免有疑问:获取listp
有有什么用呢?宏定义中的sDataLists
又是什么东西呢?
sDataLists结构分析
我们之前学习哈希表
的时候,会根据哈希函数
获取下标
,如果发生冲突再哈希
;那么现在除了再哈希
之后,还有一种拉链法
;
首先,sDataLists
是一个全局性的静态变量,也是一张哈希表,在整个SyncData
的处理过程中都会使用它;标的泛型是SyncList
,SyncList
中也含有SyncData
:
为了分析其具体结构,接下来我们使用下边代码,结合lldb
进行分析:
需要注意的是,此处我们在main
方法最后使用了do-while
的一个死循环来卡着App,否则直接直接程序就运行结束了;
我们使用p sDataLists
来查看sDataLists
的数据结构,打印之后我们发现sDataLists
结构中含有一个array
的结构,而这个array
有64
个元素,因为我们使用的是模拟器,所以打印之后有64
个结构,而此处的64
来源于:
模拟器有64个,真机是有8个;
接下来,我们让id2data
执行完毕一次(在sDataLists
中存入一个SyncData
),断住第二次执行的流程:
发现,存储的第一个SyncData
对象,保存在了23
的位置(因为是哈希结构,所以第一次并不是从0
开始,而是通过一个计算出的下标开始存储);
其结构如下:
SyncData
在SyncList
中;SyncList
在array
表中;
其数据结构大致如下(我们以真机8
个来进行图示):
我们在使用@synchronized
时,经常喜欢传self
,那么如果前后两次调用@synchronized
时都传的是self
,那么前后两个self
,生成的两个SyncData
将会产生哈希冲突,SyncList
作为链表形式
,其存储结构将会变为:
- 同一个
self
生成的两个SyncData
并不会两两冲突,将会存储进链表SyncList
中; - 两个
SyncData
具备相似性;
链表结构并不方便查询,而SyncData
并不需要查询,只需要加锁解锁,也就是只需要增删操作(拉链法);
未完待续……