前言
一直以来都有去通过blog去了解一下技术的问题,但是因为blog毕竟是别人消化的东西,在作者阅读相关资料 -> 消化 -> blog文字输出 -> 自己读blog -> 自己消化,这是一个很长的信息链,而这个链接每一个环节都存在信息漏斗,到最终到自己消化的时候,可能和信息最初的样子,会有比较大的偏差了。
还有另外一个问题就是,读取别人的blog,自己没有亲身实践过,看的时候理解了,但是总是不深刻,到后面遇到这个知识点的时候,需要比较长的时间去慢慢回忆起来,组合成一个大概的信息具象,没有一个系统的了解。
高中的时候,数学老师一直在强调好记性不如烂笔头,一直叫我们就算看懂了,你写下来之后理解会加深,现在深有体会。现在不是好记性不如烂笔头,像我这种记性不好的,需要烂键盘的!!
所以,在以后的工作还是需要作出一点点改变,看到需要不理解的知识点,是需要从信息的根来源进行寻根问题,并且记下来用文字表达一次,才能对相关知识点有一个系统行的理解。
吃过的亏,要接受教训啊!
所以这篇文章是在理解property,是从runtime和CoreFoundation出发去理解,理解其原理,能有更深刻的理解。
runtime的getter实现
在很多书籍或者blog里面,都有解析到Objective-C中各个修饰属性的区别,因为都是一个结论给出,并没有理解里面的具体实现,所以并不深刻。所以,在runtime层面查看对象属性赋值的实现。
在runtime的源代码中,在Private Header下的objc_abi.h文件中,可以找到对象属性读写的abi定义,而实际的实现代码存放在objc-accessors.mm文件中。由于这是私有abi,在iOS的SDK里面的/usr/include/目录下并不能看到这些信息。如果需要查看这个定义,需要在runtime的源代码才能看到。
在这个objc-accessors.mm文件中,先看getter方法对应的实现:
//getter函数参数与定义的修饰词相关的就只有atomic修饰词
//也就是说除了atomic修饰词和nonatomic修饰词,copy、assign、strong、weak修饰词都并无相关
id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
if (offset == 0) {
return object_getClass(self);
}
// Retain release world
id *slot = (id*) ((char*)self + offset);//计算属性所在的指针偏移量
if (!atomic) return *slot;//如果是非原子性操作,直接返回属性的对象指针
//原子性操作,则继续执行
//获取属性锁,属性锁的定义是这样的
//PropertyLocks是一个StripedMap<spinlock_t>类型的全局变量
//而StripedMap是一个用数组来实现的hashmap,key是指针,value是类型是spinlock_t对象
//而spinlock_t则是mutex_tt<LOCKDEBUG>的类,而mutex_tt类内部是由os_unfair_lock mLock来实现
//一言以蔽之,PropertyLocks[slot]目的就是获取os_unfair_lock对象
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();//加锁
id value = objc_retain(*slot);//获取到的对象引用计数+1
slotlock.unlock();//解锁
// for performance, we (safely) issue the autorelease OUTSIDE of the spinlock.
//将获取到的对象注册到自动释放池中,保证值能一定获取到,所以是线程安全的
return objc_autoreleaseReturnValue(value);
}
从函数名上就很清晰的看出来属性修饰词对应的方法,而且就是标准的objc中函数调用时,使用objc_msgSend函数进行函数调用的函数格式。前面两个是隐含参数id和_cmd,后面是函数的实际参数。
通过上述解析,那么可以总结一下getter的特性了:
- getter获取属性值,copy、assign、strong、weak修饰词无相关,只与atomic和nonatomic修饰词有关
- 使用nonatomic修饰词,获取到属性值后立马返回,效率高
- 使用atomic修饰的属性,获取过程会有加锁解锁过程,会有性能的损耗
- 使用atomic修饰的属性,会将对象注册到自动释放池中
这就是runtime环境中getter的是整个过程,相对于setter来说,比较简单。
最后的最后
上面的分析是基于属性赋值的猜测,在objc源代码的全局并没有搜索到objc_getProperty函数的调用方。在runtime中,所有的函数调用都是通过objc_msgSend函数来进行调用的,通过selector方法选择器来获取真正的IMP函数指针来执行最终的实现。而在objc源代码中,貌似没有找到IMP的定义,所以以上的分析都是基于猜测的。
今天终于将iMac降级了,能抛弃objc源代码的工程,但是写debug代码后在objc_getProperty打了断点,发现并没有调用到objc_getProperty函数,这就很困惑了。因为在调用Foundation层的setter方法,并没有具体的赋值实现的。困惑。。。