iOS Category 和 Extention 的本质区别【面试必备】
$[timeformat('2018-08-16T15:39:38+08:00')]

感谢这位博主的博客:由category为什么不能添加属性所想到的

为什么category 不能添加属性,而Extention能添加?

这个问题从几个方面来解释下:

  • 什么是OC中的属性?

    属性,是某个对象持有的特性。一般(注意这里的一般)都是在编译期编译的时候,对象持有的属性会决定对象的内存布局,编译期过后,在运行期,一般很少更改这个布局,因为运行期更改已经定好的布局,不仅会带来一系列的内存问题,而且内存开销会很大。

    打个比方:就像是买房摇号有几千人排队呢,你后来的去插队,那么就会导致后面的人的位置发生变化,并且还会激起民愤!

  • 作用时期

    Extension 是在编译期决议(起作用) Category 是在 运行期决议(起作用)

  • 作用原理

    Extention是类的一部分,能决定类的内存布局,跟对象的生命周期一致,对象生成Extention添加的属性有效,对象释放,属性释放; Category并不是真正的类,他的作用是动态的给类添加方法,不改变类的内存布局。

那么,现在再来看这个问题,是不是很好解释了呢

因为Extention是在编译期期决议,那么他就能决定类对象的内存布局,就能正常的给类添加属性。 而Category是在运行期决议的,动态的更改类,如果添加了属性,那就会更改内存布局;而内存布局是在编译期就定好了的,运行期更改内存布局会引发不必要的内存风险并且会有很大的内存开销。所以,苹果为了避免这种情况,禁止类别动态添加属性更改内存布局。

但是,为什么 runtime 动态添加属性呢?

那是因为 runtime关联的属性,并不是在真正的属性的位置添加的,而是在 AssociationsManager 管理的一个静态区内存 AssociationsHashMap 来存储关联对象的。所有用runtime 动态关联的属性都存在这个全局的map里面。map的key就是关联的对象的内存地址,而这个map的value又是另外一个AssociationsHashMap,里面保存了关联对象的键值对。

内存示意图
内存示意图

那如何销毁RunTime关联的属性呢?

runtime的销毁对象函数objc_destructInstance里面会判断这个对象有没有关联对象,如果有,会调用_object_remove_assocations做关联对象的清理工作。