必备知识架构-①语言-消息转发2

[toc]

一、NSProxy

NSProxy,你可以通过继承它,并重写它的以下两个方法以实现消息转发到另一个实例。说白了,NSProxy专为代理而生(负责将消息转发到真正的target的代理类)。从类名来看是代理类,专门负责代理对象转发消息的。相比NSObject类来说NSProxy更轻量级,通过NSProxy可以帮助Objective-C间接的实现多重继承的功能

1
2
3
- (void)forwardInvocation:(NSInvocation *)anInvocation;

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel;

1、通过NSProxy可以帮助Objective-C间接的实现多重继承的功能的例子

现在比较流行的说法是用它来模拟多重继承,大致过程就是让它Hold住/包含你要实现多继承的类的对象,然后被hold住的对象的行为定义在接口中,并让Proxy去实现这些接口。然后再转发的时候把消息转发到实现了该接口的对象去执行,这样就好像实现了多重继承一样。注意:这个真不是多重继承,只是包含,然后把消息路由到指定的对象而已,其实完全可以用NSObject类来实现。

天使=人+鸟

1
2
3
4
5
6
Person *person = [Person new];
Bird *bird = [Bird new];

id angelProxy = [AngelProxy proxyForObject1:person object2:bird];
[proxy speak]; // 人可以说话
[proxy fly]; // 鸟可以飞

另一个例子:NSProxy的学习使用

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
// AngelProxy.h
#import <Foundation/Foundation.h>

@interface AngelProxy : NSProxy

+ (id)proxyForObject1:(id)obj1 object2:(id)obj2;

@end




// AngelProxy.m
#import "AngelProxy.h"

@interface AngelProxy()

@property (nonatomic, weak) id target1;
@property (nonatomic, weak) id target2;
@end

@implementation AngelProxy

- (instancetype)initWithTarget1:(id)obj1 target2:(id)obj2 {
_target1 = obj1;
_target2 = obj2;

return self;
}

+ (id)proxyForObject1:(id)obj1 object2:(id)obj2 {
return [[AngelProxy alloc] initWithTarget1:obj1 target2:obj2];
}

// 重写NSProxy如下两个方法,在处理消息转发时,将消息转发给真正的Target处理
- (void)forwardInvocation:(NSInvocation *)invocation {
id target;
if ([_target1 methodSignatureForSelector:invocation.selector]) {
target = _target1;
}else{
target = _target2;
}
[invocation invokeWithTarget:target];
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
NSMethodSignature *sig;

sig = [_target1 methodSignatureForSelector:selector];
if (sig) {
return sig;
}

sig = [_target2 methodSignatureForSelector:selector];
return sig;
}


@end

2、NSProxy 和 NSObject 的比较

相比NSObject,NSProxy更轻量级,做消息转发效率更高。

NSObject寻找方法顺序:本类->(父类-)>(动态方法解析)-> 消息转发;
NSproxy顺序:本类->消息转发

3、NSProxy的用途

  1. AOP面向切片编程

    iOS中面向切片编程一般有两种方式 ,一个是直接基于runtime 的method-Swizzling.还有一种就是基于NSProxy

  2. 解决NSTimer, CADisplayLink等强引用target引起的无法释放问题。如NSTimer:利用消息转发来断开NSTimer对象与视图之间的强引用关系。初始化NSTimer时把触发事件的target替换成一个单独的对象,然后这个对象中NSTimer的SEL方法触发时让这个方法在当前的视图self中实现。

    1
    2
    3
    4
    self.timer = [NSTimer timerWithTimeInterval:1 target:self.proxy 
    selector:@selector(timerEvent) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
    // 这里我们timer的target使用AYProxy对象,它会实现你这个timer想要实现的timerEvent消息转发给self。从而避免了循环引用

    协议类代码为:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    #import

    @interface AYProxy : NSProxy

    @property (nonatomic, weak) id obj;

    @end

    #import "AYProxy.h"

    @implementation AYProxy

    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    NSMethodSignature *sig = nil;
    sig = [self.obj methodSignatureForSelector:aSelector];
    return sig;
    }

    - (void)forwardInvocation:(NSInvocation *)anInvocation{
    [anInvocation invokeWithTarget:self.obj];

    }

    @end
  1. 多重继承

    实现类似CAAnimation类族.