知识点汇总(二)

runtime消息转发的处理

代码在执行某一个方法时,代码执行performSelector的动作,在runtime上调用方法objc_msgSend方法(注意类方法的调用是objc_msgSendClass),执行过程如下:

1.objc_msgSend(target, selector)

target为receiver,selector为执行的方法。

Attention!!!

继承自NSObject的类都集成了一个类方法。

+(Bool)resolveInstanceMethod:(SEL)selector的方法,内部实现机制为获取实例对象的方法列表,若有,返回true,若无,返回false。

  • 当为true时,进入Event Handle处理事件;
  • 当返回为false时,说明没有实现这个方法或者返回为nil,消息进入转发Method Forwarding (消息转发);

2. - (id)forwardingTargetForSelector:(SEL)aSelector

aSelector 待处理的消息,返回一个对象,用于处理消息。

继承自NSObject的类都集成了一个实例方法。

-(id)forwardingTargetForSelector:(SEL)aSelector的方法,当类不识别消息时,执行该方法, 返回一个对象,用于处理aSelector;

  • 当为true时,将由返回的object处理事件进入objc_msgSend,继续执行,进入Event Handle处理事件;
  • 当返回为false时,说明没有实现这个方法或者返回为nil,使用最后一个挽救的方法,Normal Forwarding(正常转发);

3. - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector

消息信号,aSelector 为消息内容。

继承自NSObject的类都集成了一个实例方法。

若是类对于方法有IMP,那么调用父类的该方法就有非nil的返回,若是无IMP,则获取为nil,此时可以把把别的已经实现该方法的类的对象的信号返回,即可实现消息转发;

  • 当为true时,说明该类可以处理该方法;
  • 当为false时,说明无实现,可自行在子类实现该方法,转发消息;
  • 当为false时,且并不转发消息,那么Runtime会抛出donotRecogniseSelector的异常,程序终止运行。

4. - (void)forwardInvocation:(NSInvocation *)anInvocation

invoke NSInvocation,让target执行对应的selector.

在子类中可实现该方法,修改anInvocation的target将消息转发出去。

代码示例

为了学习并补充说明该功能,做了一个demo,展示,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#import "ViewController.h"
#import <objc/runtime.h>

#import "UIViewController+Logger.h"
#import "UIView+Logger.h"

@interface MViewStage : UIViewController

@end

@implementation MViewStage

- (void)viewDidLoad{
[super viewDidLoad];
[self performSelector:@selector(doSomething)];
}

- (id)forwardingTargetForSelector:(SEL)aSelector{
if ([NSStringFromSelector(aSelector) isEqualToString:@"doSomething"]) {
return self.parentViewController;
}
return nil;
}

//- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
// NSMethodSignature *sig = [super methodSignatureForSelector:aSelector];
// if (!sig) {
// if ([NSStringFromSelector(aSelector) isEqualToString:@"doSomething"]) {
// sig = [self.parentViewController methodSignatureForSelector:aSelector];
// }
// }
// return sig;
//
//}

- (void)forwardInvocation:(NSInvocation *)anInvocation{
if ([NSStringFromSelector(anInvocation.selector) isEqualToString:@"doSomething"]) {
[anInvocation invokeWithTarget:self.parentViewController];
}
}

@end

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.

MViewStage *stage = [[MViewStage alloc] init];
[self addChildViewController:stage];
[stage loadViewIfNeeded];

}

- (void)doSomething{
NSLog(@"ViewController : doSomething ");
}

@end

我在MViewStage 执行[self performSelector:@selector(doSomething)],但是并没有定义或者实现这个方法,因此我是用如下两种解决方法:

第一种:forwardingTargetForSelector 返回了self.parentViewController;

第二种: 注销掉forwardingTargetForSelector的实现,或者返回为nil,实现了methodSignatureForSelector和forwardInvocation方法,分别返回了self.parentViewController的methodSignatureForSelector,并在forwardInvocation执行了切换target.

以上两种均OK,都可以实现消息的转发。