runtime机制
objective-c代码总是先预编译成C代码,runtime机制也是基于C的实现。
消息机制是runtime的基础。研究runtime机制基本上就是阅读和理解对应的C代码。
-
objc_msgSend(C代码)
void objc_msgSend(id self,SEL op, ...) objc_msgSend(self,@selector(doSomethingWithVar:),var1);
-
消息转发机制
-
clang -rewrite-objc xxx.mm
-
+ (BOOL) resolveInstanceMethod:(SEL)aSEL
-
– (id)forwardingTargetForSelector:(SEL)aSelector
-
– (void)forwardInvocation:(NSInvocation *)anInvocation
-
doesNotRecognizeSelector
-
方法对换
void method_exchangeImplementations(Method m1, Method m2)
NSArray用strong修饰有什么问题?
如果不小心将一个可变数组赋值给它,将导致修改可变数组的元素的时候导致该“不可变数组”的元素发生变化。实际上指向了可变数组的内存。
NSMutableArray用copy修饰有什么问题?
初始化的时候会导致得到一个不可变数组,增加修改的时候会崩溃。doesnotreconginizeselector
循环引用
- weak解除循环引用
- block中的self并不是总是引起循环引用
如果只是block引用了self,self并不持有block,并不会引起循环引用问题。
Block
- 是一个NSObject对象
- ARC下默认copy到堆上
category使用和实现机制
- 实现原理
利用runtime动态修改 methodList 的值来添加成员方法,这也是 Category 实现的原理,因此它不能增加成员变量。添加数据可以通过关联对象来绑定一个对象。
-
使用场景
- 为已有的类增加方法
- 可以把类的实现分开在几个不同的文件里面。
a) 可以减少单个文件的体积
b) 可以把不同的功能组织到不同的category里
c) 可以由多个开发者共同完成一个类
d) 可以按需加载想要的category - 声明私有方法
- 模拟多继承
- 把framework的私有方法公开
-
extension和category的区别?
-
extension
extension在编译期决议,它就是类的一部分,在编译期和头文件里的@interface以及实现文件里的@implement一起形成一个完整的类,它伴随类的产生而产生,亦随之一起消亡。extension一般用来隐藏类的私有信息,你必须有一个类的源码才能为一个类添加extension,所以你无法为系统的类比如NSString添加extension。
extension可以添加实例变量.
- category
category是在运行期决议的。
category是无法添加实例变量的(因为在运行期,对象的内存布局已经确定,如果添加实例变量就会破坏类的内部布局,这对编译型语言来说是灾难性的)。但是通过关联对象可以绑定数据成员。
typedef struct category_t {
const char *name;
classref_t cls;
struct method_list_t *instanceMethods;
struct method_list_t *classMethods;
struct protocol_list_t *protocols;
struct property_list_t *instanceProperties;
} category_t;
上面结构表面,category可以增加实例方法,类方法,实现协议,添加属性(关联对象)。不能增加实例变量。
通过编译成C代码可以看的更清晰
clang -rewrite-objc MyClass.m
- category注意事项
- 1)、category的方法没有“完全替换掉”原来类已经有的方法,也就是说如果category和原来类都有methodA,那么category附加完成之后,类的方法列表里会有两个methodA
- 2)、category的方法被放到了新方法列表的前面,而原来类的方法被放到了新方法列表的后面,这也就是我们平常所说的category的方法会“覆盖”掉原来类的同名方法,这是因为运行时在查找方法的时候是顺着方法列表的顺序查找的,它只要一找到对应名字的方法就会停止。
+load
- 1)、附加category到类的工作会先于+load方法的执行
- 2)、+load的执行顺序是先类,后category,而category的+load执行顺序是根据编译顺序决定的。
目前的编译顺序是这样的:
代码形式 | 编译器/运行期 | 实例变量 | 和类的关系 | 类的源代码 |
---|---|---|---|---|
@interface MainViewController ()<UIGestureRecognizerDelegate | 编译器决定 | 可以增加实例变量 | 是类的一部分,跟类的产生而产生,随类的消亡一起消亡.用来隐藏类的私有信息。 | 需要类的源代码 |
@interface MainViewController (Gesture) | 运行期决定 | 不可以增加实例变量,但是可以绑定一个数据对象 | 不是类的组成部分,由运行期修改类的方法数组增加的方法 | 不需要类的源代码 |
category通过关联对象增加属性的原理
运行时通过AssociationsManager里面是由一个静态AssociationsHashMap来存储所有的关联对象的,这是一个全局的map,key是关联对象的指针地址,value是另外一个AssociationsHashMap,里面保存了关联对象的kv对。
KVC实现原理
KVC运用了一个isa-swizzling技术。isa-swizzling就是类型混合指针机制。KVC主要通过isa-swizzling,来实现其内部查找定位的。isa指针,如其名称所指,(就是is a kind of的意思),指向维护分发表的对象的类。该分发表实