iOS runtime(二)

来源:互联网 发布:js删除指定id的tr 编辑:程序博客网 时间:2024/06/10 17:18

OC运行时编程指导

参考:Apple Document:Objective-C Runtime Programming Guide
OC语言尽可能将决定从编译和链接阶段推迟到运行时去做。

  1. 运行时版本和平台
  2. 与运行时进行交互
  3. 消息发送(Messaging)
  4. 动态方法解析
  5. 消息转发(Message Forwarding)
  6. 类型编译
  7. 定义的属性

一,历史和现代版本

“现在版本”被引入到OC2.0中,突出的特性是“不易碎性”,即如果改变了一个类的实例变量,不需要重新编译集成该类的子类;而“历史版本”需要重新编译。
iPhone以及64位的OSX应用使用“现代版本”。

二,同运行时交互

三种同Runtime交互的方式

  1. 通过OC源代码
    OC源代码背后是运行时的消息传递机制。
  2. NSObject方法
    (1)类似C++多态的运行时机制。
    (2)一些NSObject的方法请求运行时信息:isKindOfClass,isMemberOfClass,respondsToSelector,conformsToProtocol,methodForSelector等。
    (3)运行时函数。

三,消息发送

  1. objc_msgSend函数
    OC中,消息在运行时才会绑定到方式实现。
    [receiver message] -> objc_msgSend(receiver, selector, arg1, arg2, …)
    消息传递函数的工作:
    (1)首先找到选择器对应的函数实现。
    (2)调用函数实现,将接受对象以及参数传给函数实现。
    (3)最终将函数实现的返回值作为自己的返回值返回。

四,动态方法解析

如何动态的提供一个方法的实现呢?

@dynamic propertyName;

上面的代码告诉编译器,该属性关联的方法是动态提供的。

可以通过实现resolveInstanceMethod:和resolveClassMethod: 方法来为实例方法和类方法动态的提供实现。
一个OC方法就是一个至少有两个参数的简单的C函数,可以通过class_addMethod为类增加方法。
所以,如果有下面的方法:

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

动态加载
一个OC程序可以在进行时加载并链接新的类和属性。新的代码被合并到程序内,跟启动时被加载的类和属性一同对待。
动态加载可以做很多事情,例如系统通用设置中的很多模块都是动态加载的。
在Cocoa环境中,动态加载通常用来允许应用被定制化。其他人可以编写你的程序运行时所加载的模块–跟IB加载用户调色板和OSX系统设置加载用户设置模块一样。被加载的模块扩展了应用的功能。他们通过你允许、但是未参与的方式实现。你提供框架,他们提供代码。

四,消息转发

将消息传递给一个不处理这个消息的对象会发生错误。但是,在抛出错误前,运行时系统接受消息的对象第二次机会去处理这个消息。
上面说的第二次机会就是运行时会给对象发送一个forwardInvocation:消息,带一个NSInvocation参数。这个对象封装了原始的消息以及传递给它的参数。
你可以实现一个forwardInvocation:方法,给消息一个默认的返回,或者用其他方式避免错误。
考虑这个场景,首先,你设计了一个可以相应negotiate消息的对象,你想要它的返回值包括另一个对象的返回值。你可以在negotiate方法的实现中,简单的通过将negotiate传递给另一个对象来实现。
更进一步,假设你想要你的对象相应negotiate消息,返回完整的返回定义在另一个类中的相应。一种方式是让你的类继承另一个类的方法。但是,这种方法可能会因为你的类跟实现negotiate的类不在一个继承层次上而不能成行。
即使不能继承negotiate方法,你也可以借用一下另一个类中的这个方法:

- (id)negotiate{    if ( [someOtherObject respondsTo:@selector(negotiate)] )        return [someOtherObject negotiate];    return self;}

这种方式有点笨,特别是如果你希望你的对象传递很多消息给其他对象时,你需要去实现你想覆盖的每一个方法。另外,你不可能处理在写代码时还不知道的情况。这就依赖于运行时的事件,这将作为未来实现的类和方法。
当一个对象由于没有方法匹配消息中的选择器而无法响应消息时,运行时系统通过发送一个forwardInvocation:消息通知该对象,但是NSObject版本的方法简单调用了doesNotRecognizeSelector:,所以你需要去实现它。

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

注意:forwardInvocation只在当前对象的现有方法无法处理消息时,才能被执行。

多继承
多继承
Warrior对象继承了Warrior,仿佛同时继承了Diploment.

代理对象
消息传递和继承
虽然消息传递可以模拟继承,但是NSObject类并没有将他们混淆。考虑respondsToSelector:和isKindOfClass:方法,如果给一个Warrior对象发送respondsToSelector:方法,返回的是NO。可以重载该方法以返回YES。
除了respondsToSelector: 和 isKindOfClass:,instancesRespondToSelector:方法也应该被考虑。

- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector{    NSMethodSignature* signature = [super methodSignatureForSelector:selector];    if (!signature) {       signature = [surrogate methodSignatureForSelector:selector];    }    return signature;}

注意:
1,只有实现了methodSignatureForSelector,forwardInvocation才会被调用。
2,头文件#import