博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
消息转发
阅读量:5245 次
发布时间:2019-06-14

本文共 3912 字,大约阅读时间需要 13 分钟。

下图是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  @interface Book : NSObject{    NSMutableDictionary *data;}//声明了两个setter/getter@property (retain) NSString *title; @property (retain) NSString *author;@end @implementation Book@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"];    }    return self;} - (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抛出异常。

 

参考BigShow目录:

07-零散知识点--->05-运行时--->06-消息转发

转载于:https://www.cnblogs.com/bigshow1949/p/6109798.html

你可能感兴趣的文章
poj 3140 树形去边差异最小
查看>>
帮助菜单没有诊断菜单
查看>>
单例设计模式
查看>>
Android开发 - 下拉刷新和分段头悬停列表
查看>>
Google搜索技巧-入门篇
查看>>
获得日期在月和年中的周
查看>>
jsp动态生成验证码的类
查看>>
Javascript中window的方法
查看>>
修改Oracle数据库表单类型
查看>>
基于lvm2的备份
查看>>
【Java 学习笔记】 Hashmap
查看>>
Git打包文件
查看>>
学习练习 java 程序设计园的周长面积
查看>>
ES6 函数的扩展
查看>>
Node.js学习笔记(1):Node.js快速开始
查看>>
中文词频统计
查看>>
圆的渐开线
查看>>
HTML5开辟无需下载的移动应用时代
查看>>
sharepoint多个NLB的web前段如何进行文件同步?
查看>>
java+selenium元素定位和元素操作
查看>>