RunTime
runtime 是什么
- runtime ,运行时机制,是一套底层 C 语言的 API,其为 iOS 内核的一部分。
- runtime 会把我们的 Objc 代码全部转换成 C 语言。
Runtime 的相关知识
-
SEL
- 方法选择器,对方法进行包装,然后找到方法对应的实现。
- 其实它就是个映射到方法的 C 字符串
- 其结构是
typedef struct objc_selector *SEL;
- 可以用 Objective-C 编译器命令
@selector()
或者 Runtime 系统的sel_registerName
函数来获得一个 SEL 类型的方法选择器
-
id
- id 是一个结构体指针类型,它可以指向 Objective-C 中的任何对象。
- 其结构是
struct objc_object { Class isa OBJC_ISA_AVAILABILITY;}
- 其中 isa 指针是指向对象的类
Class
其结构是
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE; //指向其超类。
const char *name OBJC2_UNAVAILABLE; //类名
long version OBJC2_UNAVAILABLE; //类的版本信息
long info OBJC2_UNAVAILABLE; //类的详情
long instance_size OBJC2_UNAVAILABLE; //该类的实例对象的大小
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; //成员列表
struct objc_method_list **methodLists OBJC2_UNAVAILABLE; //方法列表
struct objc_cache *cache OBJC2_UNAVAILABLE; //缓存
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; //协议列表
#endif
} OBJC2_UNAVAILABLE;
- Method
- method_types,存放方法的参数类型和返回值类型
- IMP 指向的是方法的实现,本质是一个函数指针
typedef struct objc_method *Method;
struct objc_method
{
SEL method_name ;
char *method_types ;
IMP method_imp ;
}
一个对象方法的调用的过程
- runtime 把我们调用的方法转化为 objc_msgSend,然后把方法的调用者和方法选择器当作参数传递过去。
- 通过调用者的 isa 指针,找到对应的类。
- 先看看当前类的 cache 中有没有该方法的调用记录,有就指直接加载,通过方法的 IMP 指针找到方法对应的实现。
- 如果 cache 没有,就在 methodList 找该方法。
- 如果该类中找不到对应的方法,就往父类找,直到 NSObject 这个类。
- 到 NSObject 还没有找到该类,就会进入动态方法解析。
消息转发过程
调用一个不存在的方法,程序就会 crash,在程序进入 crash 之前,会有一个消息转发的过程,如果方法还是没有被处理,程序才会 crash。
- 进入
resolveInstanceMethod:
和resolveClassMethod:
方法---动态方法解析,如果这两个方法返回的是 nil,进入下一步。 - 进入
forwardingTargetForSelector:
方法---对象重定向,用于指定对象来响应这个 SEL,若返回的是 self 或者是 nil ,则进入下一步消息转发。 - 我们首先要通过
methodSignatureForSelector:
来指定方法签名,若返回 nil,表示不处理,不会进入消息转发机制;若返回方法签名,则会进入下一步。 - 当
methodSignatureForSelector:
方法返回方法签名后,就会调用forwardInvocation:
方法---消息转发,我们可以通过 anInvocation 对象做很多处理,比如修改实现方法,修改响应对象等。
动态方法解析
+ (BOOL)resolveInstanceMethod:(SEL)aSEL
{
if (aSEL == @selector(resolveThisMethodDynamically)) {
class_addMethod([self class], aSEL, (IMP)
dynamicMethodIMP, "v@:");
return YES;
}
return [super resolveInstanceMethod:aSEL];
}
- (void)dynamicMethodIMP{
do something;
}
说明一下
class_addMethod(Class cls, SEL name, IMP imp, const char *types)
中的参数
- Class cls:类,哪个类去调用
- SEL name:方法选择器
- IMP imp:指向方法实现的指针
- const char *types:表示返回值和参数,例如"v@:"代表"返回值是 void,没有参数";"i@:@"代表"返回值是 int,参数类型为 id"。
对象重定向
- (id)forwardingTargetForSelector:(SEL)aSelector
{
if(aSelector == @selector(mysteriousMethod:)){
return alternateObject;
}
return [super forwardingTargetForSelector:aSelector];
}
消息转发
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
if ([someOtherObject respondsToSelector:
[anInvocation selector]])
[anInvocation invokeWithTarget:someOtherObject];
else
[super forwardInvocation:anInvocation];
}
- 在 forwardInvocation: 消息发送前,Runtime 系统会向对象发送methodSignatureForSelector: 消息,并取到返回的方法签名用于生成 NSInvocation 对象,所以我们也需要重写
methodSignatureForSelector:
返回签名。
方法中隐藏的参数
- 当 objc_msgSend 找到对应方法的实现时,会把消息中的所有参数传给方法的实现,还加上两个隐藏的参数:
- self 当前方法的对象指针
- SEl 方法选择器
当我们调用[super method]
时的过程
- 构造 objc_super 结构体
struct objc_super { id receiver; Class super_class; }
- 其中 receiver 仍然是 self 本身
- super_class 父类
- 因为结构体有 super_class 这个成员变量,系统就会调用
objc_msgSendSuper
这个方法,然后把将这个结构体和方法选择器都传递过去。 - 从 objc_super 结构体指向的 superClass 的方法列表开始找 setName 的 selector,找到后再以 objc_super->receiver 去调用这个 selector
即还是 self 去调用父类的方法
推荐一篇关于对象关联的贴子,写得很好
http://www.jianshu.com/p/79479a09a8c0#%E5%85%B3%E8%81%94%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%BA%94%E7%94%A8