iOS runtime等底层知识

resolveInstanceMethod和resolveCla

2016-03-08  本文已影响4217人  Crazy2015

如果我们在Objective-C中,向一个对象发送无法它无法处理的消息,会出现什么情况?
我们知道发送消息是通过,objc_msgSend(id, SEL, ...)来实现的。首先会在对象的类对象的cache,method list以及父类对象的cache,method list依次查找SEL对应的IMP。

如果没有找到,并且实现了动态方法决议机制就会决议。如果没有实现动态决议机制或者决议失败且实现了消息转发机制。就会进入消息转发流程。否则程序Crash.

也就是说如果同时实现了动态决议和消息转发。那么动态决议先于消息转发。只有当动态决议无法决议selector的实现,才会尝试进行消息转发。

一,向一个对象发送该对象无法处理的消息
@interface Foo : NSObject  
  
-(void)Bar;  
  
@end  
  
@implementation Foo  
  
-(void)Bar  
{  
    NSLog(@" >> Bar() in Foo");  
}  
  
@end  
  
/////////////////////////////////////////////////  
#import "Foo.h"  
  
int main (int argc, const char * argv[])  
{  
  
    @autoreleasepool {  
          
        Foo * foo = [[Foo alloc] init];  
          
        [foo Bar];  
          
        [foo MissMethod];  
          
        [foo release];  
    }  
    return 0;  
}  

-[Foo MissMethod]: unrecognized selector sent to instance 0x10010c840  
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Foo MissMethod]: unrecognized selector sent to instance 0x10010c840'  
*** Call stack at first throw:  
......  
terminate called after throwing an instance of 'NSException'  

对象无法处理 MissMethod 对应的 selector,也就是没有相应的实现。

二,动态方法决议

Objective C 提供了一种名为动态方法决议的手段,使得我们可以在运行时动态地为一个 selector 提供实现。我们只要实现 +resolveInstanceMethod: 或 +resolveClassMethod: 方法,并在其中为指定的 selector 提供实现即可(通过调用运行时函数 class_addMethod 来添加)。这两个方法都是 NSObject 中的类方法,其原型为:

+ (BOOL)resolveClassMethod:(SEL)name;  
+ (BOOL)resolveInstanceMethod:(SEL)name;  

参数 name 是需要被动态决议的 selector;返回值文档中说是表示动态决议成功与否。但在上面的例子中(不涉及消息转发的情况下),如果在该函数内为指定的 selector 提供实现,无论返回 YES 还是 NO,编译运行都是正确的;但如果在该函数内并不真正为 selector 提供实现,无论返回 YES 还是 NO,运行都会 crash,道理很简单,selector 并没有对应的实现,而又没有实现消息转发。
resolveInstanceMethod 是为对象方法进行决议,
而 resolveClassMethod 是为类方法进行决议。

下面我们用动态方法决议手段来修改上面的代码:

#import "Foo.h"  
#include <objc/runtime.h>  
  
void dynamicMethodIMP(id self, SEL _cmd) {  
    NSLog(@" >> dynamicMethodIMP");  
}  
  
@implementation Foo  
  
-(void)Bar  
{  
    NSLog(@" >> Bar() in Foo");  
}  
  
+ (BOOL)resolveInstanceMethod:(SEL)name  
{     
    NSLog(@" >> Instance resolving %@", NSStringFromSelector(name));  
      
    if (name == @selector(MissMethod)) {  
        class_addMethod([self class], name, (IMP)dynamicMethodIMP, "v@:");  
        return YES;  
    }  
      
    return [super resolveInstanceMethod:name];  
}  
  
+ (BOOL)resolveClassMethod:(SEL)name  
{  
    NSLog(@" >> Class resolving %@", NSStringFromSelector(name));  
      
    return [super resolveClassMethod:name];  
}  
  
@end  

其实,objective-c的方法就是至少带有两个参数(self和cmd)的普通的C函数。
因此在上面的代码中提供这样一个 C 函数 dynamicMethodIMP,让它来充当对象方法 MissMethod 这个 selector 的动态实现

因为 MissMethod 是被对象所调用,所以它被认为是一个对象方法,因而应该在 resolveInstanceMethod 方法中为其提供实现。

通过调用

class_addMethod([self class], name, (IMP)dynamicMethodIMP, "v@:"); 

就能在运行期动态地为 name 这个 selector 添加实现:dynamicMethodIMP。class_addMethod 是运行时函数,所以需要导入头文件:objc/runtime.h。

转载:http://blog.csdn.net/kesalin/article/details/8184914

上一篇 下一篇

猜你喜欢

热点阅读