ObjCRuntimeGuide小记(2)

其中,methodForSelector:是由Cocoa Runtime System提供的,而不是Objective-C本身的语言特性。这里需要注意转换过程中函数类型的正确性,包括返回值和参数,而且这里的前两个参数需要显示声明为id和SEL。

方法的动态决议

有时候我们想要为一个方法动态地提供实现,比如Objective-C的@dynamic指示符,它告诉编译器与属性对应的方法是动态提供的。我们可以利用resolveInstanceMethod:和resolveClassMethod:分别为对象方法和类方法提供动态实现。

一个Objective-C方法本质上是一个拥有至少两个参数(self和_cmd)的C函数,我们可以利用class_addMethod向一个类添加一个方法(虽然文档没写,但个人认为就是向dispatch table添加一个键值对)。

比如对于下面的函数:

//////////////////////////////////////////////////////////////    void dynamicMethodIMP(id self, SEL _cmd) {        // implementation ….    }   //////////////////////////////////////////////////////////////  

我们可以利用resolveInstanceMethod:将它添加成一个方法(比如叫resolveThisMethodDynamically):

//////////////////////////////////////////////////////////////    @implementation MyClass   + (BOOL)resolveInstanceMethod:(SEL)aSEL   {        if (aSEL == @selector(resolveThisMethodDynamically)) {             class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");             return YES;        }        return [super resolveInstanceMethod:aSEL];   }   @end   //////////////////////////////////////////////////////////////  

动态决议和发送消息并不冲突,在消息机制起作用之前,一个类是有机会动态决议一个方法的。当respondsToSelector:或者instancesRespondToSelector:被激活时,dynamic method resolver会优先有个机会为这个selector提供一份实现。如果实现了resolveInstanceMethod:,对于不想动态决议而想让其遵循消息转发机制的selectors,返回NO即可。

Objective-C程序可以在运行时链接新的类和category。动态加载可以用来做很多不同的事情,比如System Preferences里头各种模块就是动态加载的。尽管有运行时函数可以动态加载Objective-C模块(objc/objc-load.h中的objc_loadModules),但Cocoa的NSBundle类提供了更方便的动态加载接口。

消息转发

向一个对象发送它不处理的消息是一个错误,不过在报错之前,Runtime System给了接收对象第二次的机会来处理消息。在这种情况下,Runtime System会向对象发一个消息,forwardInvocation:,这个消息只携带一个NSInvocation对象作为参数——这个NSInvocation对象包装了原始消息和相应参数。

通过实现forwardInvocation:方法(继承于NSObject),可以给不响应的消��一个默认处理方式。正如方法名一样,通常的处理方式就是转发该消息给另一个对象:

//////////////////////////////////////////////////////////////    - (void)forwardInvocation:(NSInvocation *)anInvocation   {        if ([someOtherObject respondsToSelector:[anInvocation selector]])             [anInvocation invokeWithTarget:someOtherObject];        else             [super forwardInvocation:anInvocation];   }   //////////////////////////////////////////////////////////////  

对于不识别的消息(在dispatch table中找不到),forwardInvocation:就像一个中转站,想继续投递或者停止不处理,都由开发人员决定。

类型编码

为了支持Runtime System,编译器将返回值类型、参数类型进行编码,相应的编译器指示符是@encode。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wyzygj.html