Runtime Tips
Tip1. OC的消息机制
- 消息发送
- runtime 通过selector查找IMP的过程
id objc_msgSend ( id self, SEL op, ... );
- SEL:objc_object的结构体
- 动态解析
- 消息转发
objc_msgSend函数
- 检测这个 selector 是不是要忽略的。比如 Mac OS X 开发,有了垃圾回收就不理会 retain, release 这些函数了。
- 检测这个 target 是不是 nil 对象。ObjC 的特性是允许对一个 nil 对象执行任何一个方法不会 Crash,因为会被忽略掉。
- 如果上面两个都过了,那就开始查找这个类的 IMP,先从 cache 里面找,完了找得到就跳到对应的函数去执行。
- 如果 cache 找不到就找一下方法分发表。
- 如果分发表找不到就到超类的分发表去找,一直找,直到找到NSObject类为止。 如果还找不到就要开始进入动态方法解析了,后面会提到。
Tip2. runtime如何同个selector找到IMP地址
每个类对象中都有一个方发表,方法表中记录着方法名,参数,实现,通过方法名就可以查找到改方法。
参考 NSObject 上面的方法:
- (IMP)methodForSelector:(SEL)aSelector;
+ (IMP)instanceMethodForSelector:(SEL)aSelector;
Tip3. self & super
有一个 Son 类继承 Father 类,在Son类中 [self class] 和 [super class] 的区别?
[self class] -> Son
[super class] -> Son
两个输出结果一样, 原因:
self 是当前类的隐藏参数,指向当前类,这个毫无疑问。
super 表面上和self是类似,指向父类。但实际并非如此,super 实际上是一个 Magic Keyword,它本身是一个编译标示符,和 self 是指向同一个消息接收者,
不同点在于 super 会告诉编译器,用super调用的方法,要去父类里面查找,而不是本类。
runtime中表示
[self class]
id objc_msgSend(id self, SEL op, ...)
[super class]
id objc_msgSendSuper(struct objc_super *super, SEL op, ...)
// 其中objc_super是一个结构体:
struct objc_super {
__unsafe_unretained id receiver;
__unsafe_unretained Class super_class;
};
receiver 是消息接受者
super_class 记录的是父类是什么
先看 [self class]
实现:
- (Class)class {
return object_getClass(self);
}
当调用 [super class]
时,会转换为 objc_msgSendSuper
函数。
-
第一步:先构建
objc_supe
r结构体- 第一个参数为
self
- 第二个参数,通过
(id)class_getSuperclass(objc_getClass(“Son”))
获取父类
- 第一个参数为
-
第二步:调用
-(Class)class
函数, 先在Father
类中查找,没有class
函数,再向父类 NSObject 中查找,有class
函数,最后内部使用的是 ` objc_msgSend(objc_super->reciver, @selector(class))去调用,
objc_super->reciver即为
self,此时与
[self class]无异,所以输出结果为
Son`
Tip4. 使用runtime associate 创建的属性需要释放吗
ARC和MRC上都不需要手动释放
// 对象的内存销毁时间表
// 根据 WWDC 2011, Session 322 (36分22秒)中发布的内存销毁时间表
- 调用 -release :引用计数变为零
- 对象正在被销毁,生命周期即将结束.
- 不能再有新的 __weak 弱引用, 否则将指向 nil.
- 调用 [self dealloc]
- 子类 调用 -dealloc
- 继承关系中最底层的子类 在调用 -dealloc
- 如果是 MRC 代码 则会手动释放实例变量们(iVars)
- 继承关系中每一层的父类 都在调用 -dealloc
- NSObject 调 -dealloc
- 只做一件事:调用 Objective-C runtime 中的 object_dispose() 方法
- 调用 object_dispose()
- 为 C++ 的实例变量们(iVars)调用 destructors
- 为 ARC 状态下的 实例变量们(iVars) 调用 -release
- 解除所有使用 runtime Associate方法关联的对象
- 解除所有 __weak 引用
- 调用 free()
Tip5. OC实现多继承
- 通过代理实现
- 通过消息转发实现
Tip6. runtime如何实现weak
- 初始化:objc_initWeak
- 添加引用时:调用 objc_storeWeak 更新指针指向,解除原有对象绑定 weak_unregister_no_lock, 绑定新对象weak_register_no_lock
- 释放:clearDeallocating
Tip7. Category 的实现原理
- category 实际上是结构体,运行时决定
- 可以添加类方法,方法,协议,属性。
- 新添加的方法会在运行时倒序插入到原有的类最前面,
- Category中有同名方法,最后编译的那个方法会生效。(swift中不允许同名扩展方法)
- 作用:将一个类拆分为多个,是不相关的业务相互分离
Tip8. Category 是否能添加成员变量
Category 是不能添加成员变量的,因为在运行时实例内部的成员变量已经确定,这时再添加成员变量会破坏实例内部布局。
但是我们可以通过关联对象的方式去实现类似添加成员变量(与类本身的成员变量有本质区别),通过添加属性,自定义实现setter和getter方法
关联对象:通过objc_setAssociatedObject和objc_getAssociatedObject来关联对象,关联对象的值存在全局哈希表中,由AssociationsManager统一管理
Tip9. runtime 的具体应用
- 利用关联对象给分类添加属性
- 遍历类的所有属性,获取其私有属性
- swizzing交换方法,为系统方法添加自有实现。
- kvc字典转模型
Tip10. 怎么理解Objective-C是动态运行时语言。
-
主要是将数据类型的确定由编译时,推迟到了运行时。这个问题其实浅涉及到两个概念,运行时和多态。
-
简单来说, 运行时机制使我们直到运行时才去决定一个对象的类别,以及调用该类别对象指定方法。
-
多态:不同对象以自己的方式响应相同的消息的能力叫做多态。
-
意思就是假设生物类(life)都拥有一个相同的方法-eat;那人类属于生物,猪也属于生物,都继承了life后,实现各自的eat,但是调用是我们只需调用各自的eat方法。