函数调用

王朝学院·作者佚名  2016-08-27
窄屏简体版  字體: 小  |  中  |  大  |  超大  

函数调用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。

实现的例子:

//全局函数voiddynamicMethodIMP(idself, SEL _cmd)

{//implementation ....}@implementationMyTestObject//…//类函数+(BOOL) resolveInstanceMethod:(SEL)aSEL

{if(aSEL ==@selector(resolveThisMethodDynamically))

{

class_addMethod([selfclass], aSEL, (IMP) dynamicMethodIMP,"v@:");returnYES;

}return[super resolveInstanceMethod:aSel];

}//…@end

注意事项:

根据Demo实验,这个函数返回的BOOL值系统实现的objc_msgSend函数并没有参考,无论返回什么系统都会尝试再次用SEL找IML,如果找到函数实现则执行函数。如果找不到继续其他查找流程。

forwardingTargetForSelector:原型:

- (id)forwardingTargetForSelector:(SEL)aSelector

流程到了这里,系统给了个将这个SEL转给其他对象的机会。

返回参数是一个对象,如果这个对象非nil、非self的话,系统会将运行的消息转发给这个对象执行。否则,继续查找其他流程。

实现示例:

//转发目标类@interfaceNoneClass : NSObject@end@implementationNoneClass+(void)load

{

NSLog(@"NoneClass _cmd: %@", NSStringFromSelector(_cmd));

}- (void) noneClassMethod

{

NSLog(@"_cmd: %@", NSStringFromSelector(_cmd));

}@end@implementationMyTestObject//…//将消息转出某对象- (id)forwardingTargetForSelector:(SEL)aSelector

{

NSLog(@"MyTestObject _cmd: %@", NSStringFromSelector(_cmd));

NoneClass*none =[[NoneClass alloc] init];if([none respondsToSelector: aSelector]) {returnnone;

}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<Foundation/Foundation.h>@interfaceBook : NSObject

{

NSMutableDictionary*data;

}//声明了两个setter/getter@PRoperty (retain) NSString *title;

@property (retain) NSString*author;@end@implementationBook

@dynamic title, author;//不自动生成实现- (id)init

{if((self =[super init])) {

data=[[NSMutableDictionary alloc] init];

[data setObject:@"Tom Sawyer"forKey:@"title"];

[data setObject:@"Mark Twain"forKey:@"author"];

}returnself;

}- (void)dealloc

{

[data release];

[super dealloc];

}- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector

{

NSString*sel =NSStringFromSelector(selector);if([sel rangeOfString:@"set"].location ==0) {//动态造一个 setter函数return[NSMethodSignature signatureWithObjCTypes:"v@:@"];

}else{//动态造一个 getter函数return[NSMethodSignature signatureWithObjCTypes:"@@:"];

}

}- (void)forwardInvocation:(NSInvocation *)invocation

{//拿到函数名NSString *key =NSStringFromSelector([invocation selector]);if([key rangeOfString:@"set"].location ==0) {//setter函数形如 setXXX: 拆掉 set和冒号key = [[key substringWithRange:NSMakeRange(3, [key length]-4)] lowercaseString];

NSString*obj;//从参数列表中找到值[invocation getArgument:&obj atIndex:2];

[data setObject:obj forKey:key];

}else{//getter函数就相对简单了,直接把函数名做 key就好了。NSString *obj =[data objectForKey:key];

[invocation setReturnValue:&obj];

}

}@end

doesNotRecognizeSelector:原型:

- (void)doesNotRecognizeSelector:(SEL)aSelector

作为找不到函数实现的最后一步,NSObject实现这个函数只有一个功能,就是抛出异常。

虽然理论上可以重载这个函数实现保证不抛出异常(不调用super实现),但是苹果文档着重提出“一定不能让这个函数就这么结束掉,必须抛出异常”。

使用场景在一个函数找不到时,Objective-C提供了三种方式去补救:

1、调用resolveInstanceMethod给个机会让类添加这个实现这个函数

2、调用forwardingTargetForSelector让别的对象去执行这个函数

3、调用methodSignatureForSelector(函数符号制造器)和forwardInvocation(函数执行器)灵活的将目标函数以其他形式执行。

如果都不中,调用doesNotRecognizeSelector抛出异常。

文章参考https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSObject_Class/Reference/Reference.html

https://www.mikeash.com/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的例子):

@implementationBook//…-(BOOL) respondsToSelector:(SEL)aSelector

{if(@selector(setTitle:) == aSelector ||@selector(title)== aSelector ||@selector(setAuthor:)== aSelector ||@selector(author)==aSelector)

{returnYES;

}return[super respondsToSelector: aSelector];

}//…@end

 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
© 2005- 王朝網路 版權所有 導航