1,OC方法的调用
OC中的方法调用
其实都是转成了objc_msgSend函数的调用
,给receiver(方法调用者)
发送了一条消息(selector方法名)
而objc_msgSend的执行流程又可以分为3个大的阶段:
1、消息发送阶段
,
2、动态方法解析阶段
,
3、消息转发阶段
1.1消息发送阶段:从类和父类的缓存列表和方法列表中查找方法
[receiver message]
在OC里面我们调用对象方法[Receiver message]
的这种模式,实际是通过调用objc_msgSend(Receiver,message,…)
函数来找到方法的实现入口。objc_msgSend实现原理
是通过对象对应的objc_object的ISA
找到该类对应的objc_class结构体
。通过依次遍历objc_cache
,objc_method_list里面的方法找到方法实际入口
,如果没有找到
,则跳到父类寻找
,以此类推如果最终都没有找到
就会发生动态解析阶段
。
1.2动态方法解析阶段:如果消息发送阶段没有查找到方法, 就进入动态解析阶段, 负责动态添加方法的实现.
在消息发送阶段我们类或者父类中仍然没有找到方法的实现
,那么系统会去看我们是否是动态实现了该方法
,如果是实例方法,会调用对象所在类的+ (BOOL)resolveInstanceMethod:(SEL)sel
,如果是类方法,会调用 + (BOOL)resolveClassMethod:(SEL)sel
。
Person.h
#import <Foundation/Foundation.h>
@interface Person : NSObject
@end
Person.m
#import "Person.h"
#import <objc/runtime.h>
void setNameImp(id self, SEL _cmd, NSString *name){
NSLog(@"%@",name);
}
@implementation Person
+ (BOOL)resolveInstanceMethod:(SEL)sel{
NSLog(@"%s",__FUNCTION__);
if(sel == @selector(setName:)){
class_addMethod(self, sel, (IMP)setNameImp, "v@:@");
return YES;
}
return [super resolveInstanceMethod:sel];
}
@end
main.m
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p = [[Person alloc] init];
[p performSelector:@selector(setName:) withObject:@"百客"];
}
return 0;
}
1.3消息转发阶段:如果前两个阶段都没有找到方法, 就会进入消息转发阶段, 将消息转发给可以处理消息的消息接受者来处理.
1.3.1转移消息的接收者
- (id)forwardingTargetForSelector:(SEL)aSelector
通过- forwardingTargetForSelector:
可以转移消息的接收者,如果有别的对象可以实现该方法,就直接让那个对象来处理该消息。不管另一对象的方法是公有还是私有,只要实现了就行
1.3.2完整转发
如果上述方式没有对转发的消息做处理,那么系统就会走完整的转发流程。系统会先通过 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
方法,来询问一个方法签名,如果这个方法返回了正确的方法签名, 就会调用- (void)forwardInvocation:(NSInvocation *)anInvocation
方法. 如果未能找到就会调用-[NSObject(NSObject) doesNotRecognizeSelector:]
,
所以消息转发总结如下:
ps:详细解析推荐:
iOS runtime探究(二): 从runtime开始深入理解OC消息转发机制