Objective-C是一门动态语言,一个函数是由一个selector(SEL),和一个implement(IML)组成的。Selector相当于门牌号,而Implement才是真正的住户(函数实现)。和现实生活一样,门牌可以随便发(@selector(XXX)),但是不一定都找得到住户,如果找不到系统会给程序几次机会来程序正常运行,实在没出路了才会抛出异常。下图是objc_msgSend调用时,查找SEL的IML的过程。咱们以这个流程为例看看其中涉及的很有用的函数。 图:运行时查找函数的流程 resolveInstanceMethod函数原型: + (BOOL)resolveInstanceMethod:(SEL)name 这个函数在运行时(runtime),没有找到SEL的IML时就会执行。这个函数是给类利用class_addMethod添加函数的机会。 根据文档,如果实现了添加函数代码则返回YES,未实现返回NO。 实现的例子: //全局函数void dynamicMethodIMP(id self, SEL _cmd){ // implementation ....}@implementation MyTestObject//…//类函数+ (BOOL) resolveInstanceMethod:(SEL)aSEL{ if (aSEL == @selector(resolveThisMethodDynamically)) { class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, 'v@:'); return YES; } return [super resolveInstanceMethod:aSel];}//…@end 注意事项: 根据Demo实验,这个函数返回的BOOL值系统实现的objc_msgSend函数并没有参考,无论返回什么系统都会尝试再次用SEL找IML,如果找到函数实现则执行函数。如果找不到继续其他查找流程。 forwardingTargetForSelector:原型: - (id)forwardingTargetForSelector:(SEL)aSelector 流程到了这里,系统给了个将这个SEL转给其他对象的机会。 返回参数是一个对象,如果这个对象非nil、非self的话,系统会将运行的消息转发给这个对象执行。否则,继续查找其他流程。 实现示例: //转发目标类@interface NoneClass : NSObject@end@implementation NoneClass+(void)load{ NSLog(@'NoneClass _cmd: %@', NSStringFromSelector(_cmd));}- (void) noneClassMethod{ NSLog(@'_cmd: %@', NSStringFromSelector(_cmd));}@end@implementation MyTestObject//…//将消息转出某对象- (id)forwardingTargetForSelector:(SEL)aSelector{ NSLog(@'MyTestObject _cmd: %@', NSStringFromSelector(_cmd)); NoneClass *none = [[NoneClass alloc] init]; if ([none respondsToSelector: aSelector]) { return none; } return [super forwardingTargetForSelector: aSelector];}//…@end 当执行MyTestObject对象执行[myTestObject nonClassMethod]函数时,消息会抛到NoneClass对象中执行。
methodSignatureForSelector:原型: - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector 这个函数和后面的forwardInvocation:是最后一个寻找IML的机会。这个函数让重载方有机会抛出一个函数的签名,再由后面的forwardInvocation:去执行。 forwardInvocation:原型: - (void)forwardInvocation:(NSInvocation *)anInvocation 真正执行从methodSignatureForSelector:返回的NSMethodSignature。在这个函数里可以将NSInvocation多次转发到多个对象中,这也是这种方式灵活的地方。(forwardingTargetForSelector只能以Selector的形式转向一个对象) 下面这个示例代码,诠释了这种实现优势: #import doesNotRecognizeSelector:原型: - (void)doesNotRecognizeSelector:(SEL)aSelector 作为找不到函数实现的最后一步,NSObject实现这个函数只有一个功能,就是抛出异常。 虽然理论上可以重载这个函数实现保证不抛出异常(不调用super实现),但是苹果文档着重提出“一定不能让这个函数就这么结束掉,必须抛出异常”。 使用场景在一个函数找不到时,Objective-C提供了三种方式去补救: 1、调用resolveInstanceMethod给个机会让类添加这个实现这个函数 2、调用forwardingTargetForSelector让别的对象去执行这个函数 3、调用methodSignatureForSelector(函数符号制造器)和forwardInvocation(函数执行器)灵活的将目标函数以其他形式执行。 如果都不中,调用doesNotRecognizeSelector抛出异常。 文章参考https://www./pyblog/friday-qa-2009-03-27-objective-c-message-forwarding.html http://blog.csdn.net/yiyaaixuexi/article/details/8970734
补充:respondsToSelector原型: + (BOOL)respondsToSelector:(SEL)aSelector 这个函数大家再熟悉不过了,用来检查对象是否实现了某函数。 此函数通常是不需要重载的,但是在动态实现了查找过程后,需要重载此函数让对外接口查找动态实现函数的时候返回YES,保证对外接口的行为统一。 示例代码(接forwardInvocation的例子): @implementation Book//…- (BOOL) respondsToSelector:(SEL)aSelector{ if (@selector(setTitle:) == aSelector || @selector(title) == aSelector || @selector(setAuthor:) == aSelector || @selector(author) == aSelector) { return YES; } return [super respondsToSelector: aSelector];} //…@end
|
|
来自: shijingfulwpqz > 《待分类》