NSObject的一些方法
+ (BOOL)resolveClassMethod:(SEL)sel ;
+ (BOOL)resolveInstanceMethod:(SEL)sel ;
这两个函数在运行时,没有找到SEL的IMP时就会执行,一个是类方法,一个是实例方法。这两个函数是给类利用class_addMethod添加函数的机会,如果实现了添加函数代码则返回YES,未实现返回NO。
- (id)forwardingTargetForSelector:(SEL)aSelector ;
这个方法是系统给了个将这个SEL转给其他对象的机会。 返回参数是一个对象,如果这个对象非nil、非self的话,系统会将运行的消息转发给这个对象执行。否则,继续查找其他流程
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector ;
这个函数和后面的forwardInvocation:
是最后一个寻找IMP的机会。这个函数让重载方有机会抛出一个函数的签名,再由后面的forwardInvocation:
去执行。
- (void)forwardInvocation:(NSInvocation *)anInvocation ;
在这个函数里可以将NSInvocation多次转发到多个对象中,这也是这种方式灵活的地方。(forwardingTargetForSelector
只能以Selector的形式转向一个对象)
- (void)doesNotRecognizeSelector:(SEL)aSelector;
作为找不到函数实现的最后一步,NSObject实现这个函数只有一个功能,就是抛出异常。虽然理论上可以重载这个函数实现保证不抛出异常(不调用super实现),但是苹果文档着重提出“一定不能让这个函数就这么结束掉,必须抛出异常”。
方法转发流程
下面一张图详细的概括了每个函数调用的先后以及执行的前提
Objective-C是一门动态语言,一个函数是由一个selector(SEL)
,和一个implement(IMP)
组成的。Selector相当于地址,而Implement则是真实的地点(函数实现)。
和现实生活一样,地址可以随便写(@selector(XXX)),但是不一定都找得到这个地点,如果找不到系统会给程序几次机会来程序正常运行,实在没出路了才会抛出异常。
对应流程图就是:
在一个函数找不到时,Objective-C提供了三种方式去补救:
1、调用resolveInstanceMethod
给个机会让类添加这个实现这个函数
2、调用forwardingTargetForSelector
让别的对象去执行这个函数
3、调用methodSignatureForSelector
(函数符号制造器)和forwardInvocation
(函数执行器)灵活的将目标函数以其他形式执行。
4、如果都不中,调用doesNotRecognizeSelector
抛出异常。
流程验证
定义一个类Runing
@interface Runing : NSObject
-(void)myClassA;
-(void)myClass;
@end
@implementation Runing
+ (BOOL)resolveInstanceMethod:(SEL)sel{
//运行时,可在此动态添加方法
NSLog(@" %s",__func__);
return NO;
}
- (id)forwardingTargetForSelector:(SEL)aSelector{
//在此可实现消息转发给他的对象
NSLog(@" %s",__func__);
return nil;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
NSLog(@" %s",__func__);
//return nil;
NSMethodSignature *methodSignature = [NSMethodSignature signatureWithObjCTypes:"v@:@"];
return methodSignature;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
NSLog(@" %s",__func__);
//若不调用super,找不到对应的方法实现则抛弃方法,结束流程!
[super forwardInvocation:anInvocation];
}
- (void)doesNotRecognizeSelector:(SEL)aSelector{
//若不调用super方法,则不会抛出异常
NSLog(@" %s",__func__);
}
-(void)myClassA{
NSLog(@"myClassA");
}
@end
然后我们调用Runing的方法
Runing *runing = [[Runing alloc] init];
[runing performSelector:@selector(myClass)];
从上面我们可以看到myClass方法我们只是定义了,但是没有去实现。
流程分析:
因为没有找到myClass的实现,所以会执行
resolveInstanceMethod
方法,再次可以动态的添加方法的实现。若不添加则继续查找。系统使用
forwardingTargetForSelector
方法,将myClass方法转给其返回值对象去执行。如果返回值是nil
或self
则继续查找。methodSignatureForSelector
方法会抛出一个函数签名,因为myClass方法没有实现,所以使用aSelector参数来抛出函数签名则会返回nil直接到流程5,所以这里用了一个固定值"v@:@",来返回一个NSMethodSignature
类型。然后则会重复流程1查找IMP,找不到才会进入下一步到
forwardInvocation
是最后一个寻找IMP的机会,它可以灵活的将目标函数以其他形式执行而不报错!如果调用super方法,则会继续执行。doesNotRecognizeSelector
最后没有识别myClass方法,则抛弃并不抛出异常。如果实现该方法的super方法则会抛出异常!