iOS基础iOS底层

详解NSProxy

2021-09-03  本文已影响0人  行知路

一、概述

        NSProxy是少有的没有从NSObject类继承的类,它实现了NSObject协议。其日常使用不多,但是也是有一些比较好的使用场景。

// 实现了NSObject协议
@interface NSProxy <NSObject> {
    __ptrauth_objc_isa_pointer Class    isa;
}

+ (id)alloc;
+ (id)allocWithZone:(nullable NSZone *)zone NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
+ (Class)class;

- (void)forwardInvocation:(NSInvocation *)invocation;
- (nullable NSMethodSignature *)methodSignatureForSelector:(SEL)sel NS_SWIFT_UNAVAILABLE("NSInvocation and related APIs not available");
- (void)dealloc;
- (void)finalize;
@property (readonly, copy) NSString *description;
@property (readonly, copy) NSString *debugDescription;
+ (BOOL)respondsToSelector:(SEL)aSelector;

- (BOOL)allowsWeakReference API_UNAVAILABLE(macos, ios, watchos, tvos);
- (BOOL)retainWeakReference API_UNAVAILABLE(macos, ios, watchos, tvos);

// - (id)forwardingTargetForSelector:(SEL)aSelector;

@end

二、基本作用

        正如该类的名字所示的那样,这个类最基本的作用就是作为一个代理。通过持有其他对象并自动的把消息转发给所持有(代理)的对象类来实现代理的功能。这么说起来,都有被代理对象了,为什么要代理?其实现实世界是负责的,比如代理对象的创建代价比较昂贵。另外,在这个基本作用的基础之上可以构建更加强大的应用(本文第三小节会讲到)。

/*------------头文件-------------*/
@interface MyProxy : NSProxy

+ (instancetype)proxyWithTarget:(NSObject *)target;

@end

/*------------实现文件-------------*/
@interface MyProxy ()

@property (nonatomic, strong) NSObject *target;

@end

@implementation MyProxy

// NSProxy 没有初始化方法,需要我们自己实现
- (instancetype)initWithTarget:(NSObject *)target {
    _target = target;
    return self;
}

+ (instancetype)proxyWithTarget:(NSObject *)target {
    MyProxy *proxy = [[MyProxy alloc] initWithTarget:target];
    return proxy;
}

// 获得调用方法的方法签名,必须实现的方法
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    NSMethodSignature *signature;
    if (_target) {
        signature = [_target methodSignatureForSelector:sel];
    } else {
       signature= [super methodSignatureForSelector:sel];
    }
    return signature;
}

// 为调用设置目标,必须实现的方法
- (void)forwardInvocation:(NSInvocation *)invocation {
    if (!_target) {
        return;
    }
        
    [invocation invokeWithTarget:_target];
}

@end

/*------------测试代码-------------*/
NSString *aStr = (NSString *)[MyProxy proxyWithTarget:@"here"];
[aStr length];

// 看起来测试代码平淡无奇,但是需要注意的是:aStr能够响应不属于自己的消息
// aStr 虽然进行了类型转换,但是它实质上是MyProxy类型

三、实际应用

3.1 解除循环引用

        我们都知道NSTimer引起循环引用的场景,以VC含有Timer,Timer持有VC为例,循环引用如下图所示。


timer的循环引用

        解决此问题的方法也很简单,使用如下代码。

/*------------头文件-------------*/
@interface MyProxy : NSProxy

+ (instancetype)proxyWithTarget:(NSObject *)target;

@end

/*------------实现文件-------------*/
@interface MyProxy ()

@property (nonatomic, weak) NSObject *target;

@end

@implementation MyProxy

// NSProxy 没有初始化方法,需要我们自己实现
- (instancetype)initWithTarget:(NSObject *)target {
    _target = target;
    return self;
}

+ (instancetype)proxyWithTarget:(NSObject *)target {
    MyProxy *proxy = [[MyProxy alloc] initWithTarget:target];
    return proxy;
}

// 获得调用方法的方法签名,必须实现的方法
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    NSMethodSignature *signature;
    if (_target) {
        signature = [_target methodSignatureForSelector:sel];
    } else {
       signature= [super methodSignatureForSelector:sel];
    }
    return signature;
}

// 为调用设置目标,必须实现的方法
- (void)forwardInvocation:(NSInvocation *)invocation {
    if (!_target) {
        return;
    }
        
    [invocation invokeWithTarget:_target];
}

@end

/*------------实现文件-------------*/

// 创建timer
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:[MyProxy proxyWithTarget:self] selector:@selector(timerEvent) userInfo:nil repeats:YES];

// 销毁timer
-(void)dealloc {
  [_timer invalidate];
}

        源码之前了无秘密!其解除循环依赖本质上依赖的是weak,但是由于其具有消息转发作用,所以对原有代码逻辑改动很小!

解除timer的循环引用

3.2 模拟多继承

        模拟多继承也是很简单的,代码如下:

ClassA *clsA = [[ClassA alloc]init];
ClassB *clsB = [[ClassB alloc]init];

MyProxy *proxy = [SFProxy alloc];
// 变身为clsA的代理
[proxy transformToObject:clsA];
[proxy performSelector:@selector(funcA) withObject:nil];

// 变身为clsB的代理
[proxy transformToObject:clsB];
[proxy performSelector:@selector(funcB) withObject:nil];

四、与NSObject的区别

        虽然NSProxy和class NSObject都定义了-forwardInvocation:和-methodSignatureForSelector:,但这两个方法并没有在protocol NSObject中声明;两者对这俩方法的调用逻辑更是完全不同。
        对于class NSObject而言,接收到消息后先去自身的方法列表里找匹配的selector,如果找不到,会沿着继承体系去superclass的方法列表找;如果还找不到,先后会经过+resolveInstanceMethod:和-forwardingTargetForSelector:处理,处理失败后,才会到-methodSignatureForSelector:/-forwardInvocation:进行最后的挣扎.
        但对于NSProxy,接收unknown selector后,直接回调-methodSignatureForSelector:/-forwardInvocation:,消息转发过程比class NSObject要简单得多。
        相对于class NSObject,NSProxy的另外一个非常重要的不同点也值得注意:NSProxy会将自省相关的selector直接forward到-forwardInvocation:回调中,这些自省方法包括:

参考如下文章:
1、https://juejin.cn/post/6847902216209039367
2、https://juejin.cn/post/6844903922008604686

上一篇下一篇

猜你喜欢

热点阅读