标签:添加 为什么 重写 this 类的方法 sign res 不同的 流程
前面提到过编译器最终会把我们的消息发送转化为函数调用
- 通过obj的isa指针找到其所对应的类。
- 通过SEL先去类的cache列表中找这个方法,如果就去找方法的实现,不存在,进入第3步
- 去类的method列表中找,如果就去找方法的实现,没有找到,根据类中的superclass指针去父类中找,一直到NSObject.
IMP class_getMethodImplementation(Class cls, SEL name);
IMP method_getImplementation(Method m)
在id objc_msgSend(id self, SEL _cmd, ...) {
Class class = object_getClass(self);
IMP imp = class_getMethodImplementation(class, _cmd);
return imp ? imp(self, _cmd, ...) : 0;
}
可以看到这个方法中的第二行代码imp,可以通过这个imp来查找这个方法的实现,要是没有找到,runtime给我们提供了三次机会让我们的程序不会崩溃,也就是下面要提到的动态方法解析和消息转发(消息重定向,消息转发)
* 动态方法解析
resolveInstanceMethod或者resolveClassMethod给你提供一次添加方法实现的机会
下面例子,在student类中有一个没有实现的write方法,但是没有实现,如果我们掉用它,会因为找不到实现程序崩溃,有下面的挽救措施
//在这个c语言函数添加方法的实现
void test(){
NSLog(@"我是动态添加的方法");
}
//然后实现这个方法,当这个方法返回no时或者没有实现时会进入消息转发
+(BOOL)resolveInstanceMethod:(SEL)sel{
if (sel == @selector(write)) {
class_addMethod([self class], sel, (IMP)test, "v@:");
}
return [super resolveInstanceMethod:sel];
}
如你添加了函数实现并且返回了yes,那么就会调用。不会进入下面的消息转发,否则进入下面的消息转发
定义一个新类,里面添加一个和student中未实现的方法同名的方法,而且这个方法有实现,假设叫testStudent,然后在student中实现下面的方法
//该方法返回一个添加了方法实现的对象,
-(id)forwardingTargetForSelector:(SEL)aSelector{
if (aSelector == @selector(write)) {
//studentTest类中这个方法有实现
StudentTest *test = [[StudentTest alloc]init];
return test;
}
return [super forwardingTargetForSelector:aSelector];
}
//这个方法返回一个NSInvocation*的对象,里面打包了有关于这个未实现方法的信息
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
NSString*sel = NSStringFromSelector(aSelector);
if ([sel isEqualToString:@"write"]) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
-(void)forwardInvocation:(NSInvocation *)anInvocation{
SEL selector = [anInvocation selector];
StudentTest *test = [[StudentTest alloc]init];
if ([test respondsToSelector:selector]) {
[anInvocation invokeWithTarget:test];
}
}
- 根据isa指针找到对象所属的类或者类所属的元类
- 先去类或者元类的cache列表中根据SEL去找这个方法。
- 没有找到,去method方法列表中找
- 还是没有找到,就去父类中找
- 找到了,根据SEL找到对应的IMP,调用这个函数
- 没有找到,进入动态方法解析或者消息转发。
runtime提供的第一次实现这个方法的机会,要实现resolveInstanceMethod/resolveClassMethod方法,给未实现的方法在运行时添加实现,返回no/不实现,进入消息转发。
消息重定向:返回一个实现了该方法的对象,要实现-(id)forwardingTargetForSelector:(SEL)aSelector函数,如果返会nil/self,则进入下面的消息转发二
将未实现方法的相关信息打包成一个NSInvocation对象,然后交给一个类去实现。需要实现-(NSMethodSignature )methodSignatureForSelector:(SEL)aSelector和-(void)forwardInvocation:(NSInvocation )anInvocation方法
疑问?为什不直接找IMP而要通过SEL这个中间人呢?
SEL只是方法的编号,真是的实现是通过IMP来查找的,SEL和IMP之间是一一映射的关系,通过SEL我们可以改变他的IMP,然后让一个方法在不同的情况下有不同的实现,例如实现方法的交换,有时候我们需要给系统的方法添加一些自己的东西
1:可以通过一个子类继承于系统类,然后重写那个类的方法
2:通过分类,但是会覆盖系统的方法
3:写一个自己的方法,通过runtime在load方法中交换系统方法和自己的方法的实现
下面主要针对第三种例子举例:
第一步首先为我们要动手脚的系统方法类添加一个分类,
假设我们要为imageNamed添加一个判断nil的功能,先要为他添加一个分类,然后给系统的imageNamed方法添加前缀,明明一个自己的方法,如下
分类的h文件
分类的m文件,对于这里乍一看可能像递归,其实在第一次调用的系统的imageNamed方法时调用的是my_imageNamed方法,当第调用的my_imageNamed方法时其实在调用系统的imageNamed方法
交换
至于为什么要在load方法中写,我会在别的博客中提到。
runtime总结二之消息机制(包括消息转发,消息交换的黑魔法)
标签:添加 为什么 重写 this 类的方法 sign res 不同的 流程
原文地址:http://blog.csdn.net/li15809284891/article/details/54767504