最近,我正在研究Objective-C中的运行时。
我创建了一个名为TO
的小组:
@interface TO : NSObject
@end
#import "TO.h"
@implementation TO
- (id)forwardingTargetForSelector:(SEL)aSelector {
NSLog(@"%@ sel: %@", NSStringFromSelector(_cmd), NSStringFromSelector(aSelector));
return nil;
}
- (BOOL)respondsToSelector:(SEL)aSelector {
NSLog(@"%@ sel: %@", NSStringFromSelector(_cmd), NSStringFromSelector(aSelector));
return NO;
}
+ (BOOL)resolveClassMethod:(SEL)sel {
NSLog(@"%@ sel: %@", NSStringFromSelector(_cmd), NSStringFromSelector(sel));
return NO;
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
NSLog(@"%@ sel: %@", NSStringFromSelector(_cmd), NSStringFromSelector(sel));
return NO;
}
+ (IMP)instanceMethodForSelector:(SEL)aSelector {
NSLog(@"%@ sel: %@", NSStringFromSelector(_cmd), NSStringFromSelector(aSelector));
return nil;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
NSLog(@"%@", NSStringFromSelector(_cmd));
}
@end
然后,我在某处调用了一个未被识别的选择器:
TO *to = [TO new];
id res = [(NSString *)to uppercaseString];
随后,我得到了以下输出:
2015-12-22 22:27:04.319 OCDemo[81920:7728539] resolveInstanceMethod: sel: uppercaseString
2015-12-22 22:27:04.320 OCDemo[81920:7728539] forwardingTargetForSelector: sel: uppercaseString
2015-12-22 22:27:04.320 OCDemo[81920:7728539] resolveInstanceMethod: sel: uppercaseString
2015-12-22 22:27:04.320 OCDemo[81920:7728539] -[TO uppercaseString]: unrecognized selector sent to instance 0x7fdd3ad120a0
2015-12-22 22:27:04.322 OCDemo[81920:7728539] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[TO uppercaseString]: unrecognized selector sent to instance 0x7fdd3ad120a0'
如我们所见,resolveInstanceMethod:
被调用了两次。
但是,如果我先打电话给-[description]
:
TO *to = [TO new];
[to description];
id res = [(NSString *)to uppercaseString];
然后,输出将是:
2015-12-22 22:58:50.458 OCDemo[82137:7813436] resolveInstanceMethod: sel: uppercaseString
2015-12-22 22:58:50.459 OCDemo[82137:7813436] forwardingTargetForSelector: sel: uppercaseString
2015-12-22 22:58:50.459 OCDemo[82137:7813436] -[TO uppercaseString]: unrecognized selector sent to instance 0x7f9bcad59960
2015-12-22 22:58:50.461 OCDemo[82137:7813436] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[TO uppercaseString]: unrecognized selector sent to instance 0x7f9bcad59960'
此时resolveInstanceMethod:
只被调用一次。
有人可以解释一下吗?
答案 0 :(得分:1)
看起来对给定类的第一个方法调用(在您的示例中将为[to description]
)缓存类选择器。实际上resolveInstanceMethod:
仅对无法识别的选择器调用一次。
使用函数
刷新给定类的方法高速缓存时,可以看到这一点#import <objc/runtime.h>
void _objc_flush_caches(Class cls)
修改示例:
TO *to = [TO new];
[to description];
_objc_flush_caches([to class]);
id res = [(NSString *)to uppercaseString];
给出以下输出:
2017-04-15 21:48:21.575527+0200 pro objective c games[29820:1116842] resolveInstanceMethod: sel: uppercaseString
2017-04-15 21:48:21.575760+0200 pro objective c games[29820:1116842] forwardingTargetForSelector: sel: uppercaseString
2017-04-15 21:48:21.575808+0200 pro objective c games[29820:1116842] resolveInstanceMethod: sel: uppercaseString
2017-04-15 21:48:21.575833+0200 pro objective c games[29820:1116842] -[TO uppercaseString]: unrecognized selector sent to instance 0x100303600
2017-04-15 21:48:21.579989+0200 pro objective c games[29820:1116842] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[TO uppercaseString]: unrecognized selector sent to instance 0x100303600'
正如您所看到的那样resolveInstanceMethod:
被调用两次,即使先前调用过[to description]
也是如此。当您注释掉\\_objc_flush_caches([to class]);
时,resolveInstanceMethod:
将被调用一次。就像你的第二个例子。它表明您注意到的行为与方法缓存有关。