Dynamic Method Resolution
有些时候,你需要为一个方法动态提供实现。比如,OC中声明成员属性时使用@dynamic
修饰符:
@dynamic propertyName;
这会告诉编译器,这个属性相关的方法会动态实现。你可以通过实现resolveInstanceMethod:
和resolveClassMethod:
方法,为一个给定的selector来动态提供一个类方法和对象方法。
一个OC的方法仅仅是一个至少带有self
和_cmd
两个参数的C语言函数。你可以使用class_addMethod
函数来给一个类添加一个函数:
void dynamicMethodIMP(id self, SEL _cmd) {
// implementation ....
}
你可以把它当做一个方法使用resovleInstanceMethod:
动态地添加给一个类(调用resovleThisMethodDynamically
):
@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
消息转发和动态决议在很大程度上是正交的?(Forwarding methods (as described in Message Forwarding and dynamic method resolution are, largely, orthogonal.)。一个类有机会在转发机制结束之前动态处理一个方法,如果resovleInstanceMethod:
或者instancesRespondToSelector:
被调用,动态方法的处理者首先为selector提供一个IMP
。如果实现resolveInstanceMethod:
但是想指定selectors实际上被消息转发机制转发,则让selectors返回NO
。
(晦涩难懂啊我去,需要通过实践慢慢消化)
Dynamic Loading
OC程序可以在运行时加载和链接新的类和类别,被合并到程序中的新代码与一开始加载的类或类别同等对待。动态加载可以做许多不同的事。例如,App的系统设置功能模块是动态加载的。在Cocoa环境中,动态加载常用于允许App的自定义。其他的可以在运行时加载其他模块,例如 Interface Builder加载自定义调色板,和OS X系统设置应用加载自定义偏好设置模块。动态加载模块扩展了应用的功能。 Runtime 提供了动态加载的方法objc_loadModules
,但是Cocoa里的NSBundle
类为动态加载提供了更方便的接口-one that’s object-oriented and integrated with related services,可以在到这里NSBundle看看其用法。
Message Forwarding
向一个对象发送消息,如果不做处理将会发生异常。然而,在错误发生前,runtime系统会给接收者第二个机会来处理这个消息。
Forwarding
如果向一个对象发送消息而不去处理它,runtime会发送一个把NSInvocation
对象当做参数的forwardInvocation:
消息。这个NSInvocation
对象囊括了之前的消息和参数。
我们可以实现forwordInvocation:
方法来给消息一个默认的回复,或者用其它的方法来避免异常。就像名字所暗示的那样,forwardInvocation:
通常用来转发消息给另一个对象。
假设,我们首先设计一个叫做negotiate
的可以回复消息的对象,我们想要它的回复包括另一种对象的回复。你可以通过传递negotiate
消息给在实现negotiate
方法的地方的对象轻松地实现。
更进一步,我们希望我们另一个类中的对象能够准确执行negotitate
方法,可以通过继承这个类来实现。这就是为什么我们的类和实现negotiate
的类在继承关系中处在不同的分支中。
即使我们的类不能继承negotiate
方法,我们可以通过实现一个方法简单地传送消息到另一个类的实例中去“借”negotiate
方法:
- (id)negotiate {
if ([someOtherObject respondsTo:@selector(negotiate)])
return [someOtherObject negotiate];
return self;
}
这种方法显得比较笨重,尤其在我们想让我们的对象在另一个类中传递大量的消息。我们需要为每一个从另外的类“借”来的方法写一个实现。此外,对于一些未来位置的情况,我们无法做出正确的处理。
另一种方法则是使用forwardInvocation:
方法,动态方式比静态的更好。当一个对象不能回应消息,因为在消息中它没有一个匹配的selector,runtime系统通过发送一个forwardInvocation:
消息来通知对象。每一个对象都会从NSObject
类继承forwardInvocation:
方法。然而,NSObject
仅仅调用了doseNotRecognizeSelector:
。我们通过重写NSOjbect
的版本号和实现来利用forwardInvocation:
方法来实现向其他对象转发消息。
转发消息时,所有的forwardInvocation:
方法需要明确消息要发送到哪,并且将原来的参数传递过去。
像这样使用invokeWithTarget:
方法:
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
if ([someOtherObject respondsToSelector: [anInvocation selector]])
[anInvocation invokeWithTarget:someOtherObject];
else
[super forwardInvocation:anInvocation];
}
这个消息的返回值被传递到最初的发送者,包括id
类型,结构体,双精度浮点数。
forwardInvocation:
方法在无法辨认消息中饰演着分配中心的角色,打包发送给不同的接收者。或者也可以是一个中转站,将所有的消息发送到相同的目的地。它可以将一个消息转变成另一个,或者“吞掉”一些消息,所以有些时候即没有异常,也没有回应。forwardInvocation:
方法也可以将几个消息合并成一个回复。然而,它提供的在消息转发链中链接对象的机会为程序设计带来更多可能性。
Note:forwardInvocation:
只有在调用了不存在的方法导致消息无法处理时才会被调用。
网友评论