zoukankan      html  css  js  c++  java
  • 使用NSProxy和NSObject设计代理类的差异

    经常发现在一些需要使用消息转发而创建代理类时, 不同的程序员都有着不同的使用方法, 有些采用继承于NSObject, 而有一些采用继承自NSProxy. 二者都是Foundation框架中的基类, 并且都实现了<NSObject>这个接口, 从命名和文档中看NSProxy天生就是用来干这个事情的. 但即便如此, 它们却都定义了相同的消息转发的接口, 那我们在使用二者来完成这个工作时有什么差异呢.

    先贴一下通过二者来创建代理类的最基本实现代码.

    继承自NSProxy

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    @interface THProxyA : NSProxy

    @property (nonatomic, strong) id target;

    @end

    @implementation THProxyA

    - (id)initWithObject:(id)object {

        self.target = object;

        return self;

    }

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

        return [self.target methodSignatureForSelector:selector];

    }

    - (void)forwardInvocation:(NSInvocation *)invocation {

        [invocation invokeWithTarget:self.target];

    }

    @end

    继承自NSObject

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    @interface THProxyB : NSObject

    @property (nonatomic, strong) id target;

    @end

    @implementation THProxyB

    - (id)initWithObject:(id)object {

        self = [super init];

        if (self) {

            self.target = object;

        }

        return self;

    }

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

        return [self.target methodSignatureForSelector:selector];

    }

    - (void)forwardInvocation:(NSInvocation *)invocation {

        [invocation invokeWithTarget:self.target];

    }

    @end

    代码基本是一致的, 除了初始化时规范的写法有细节差异, 这个差异是因为NSProxy这个基类没有定义默认的init方法.

    1.经测试发现以下两个在<NSObject>中定义的接口, 在二者之间表现是不一致的:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    NSString *string = @"test";

    THProxyA *proxyA = [[THProxyA alloc] initWithObject:string];

    THProxyB *proxyB = [[THProxyB alloc] initWithObject:string];

    NSLog(@"%d", [proxyA respondsToSelector:@selector(length)]);

    NSLog(@"%d", [proxyB respondsToSelector:@selector(length)]);

    NSLog(@"%d", [proxyA isKindOfClass:[NSString class]]);

    NSLog(@"%d", [proxyB isKindOfClass:[NSString class]]);

    结果会输出完成不同的结论:

    1

    2

    1 0

    1 0

    也就是说通过继承自NSObject的代理类是不会自动转发respondsToSelector:和isKindOfClass:这两个方法的, 而继承自NSProxy的代理类却是可以的. 测试<NSObject>中定义的其它接口二者表现都是一致的.

    2.NSObject的所有Category中定义的方法无法在THProxyB中完成转发

    举一个很常见的例子, valueForKey:是定义在NSKeyValueCoding这个NSObject的Category中的方法, 尝试二者执行的表现.

    1

    2

    NSLog(@"%@",[proxyA valueForKey:@"length"]);

    NSLog(@"%@",[proxyB valueForKey:@"length"]);

    这段代码第一句能正确运行, 但第二行却会抛出异常, 分析最终原因其实很简单, 因为valueForKey:是NSObject的Category中定义的方法, 让NSObject具备了这样的接口, 而消息转发是只有当接收者无法处理时才会通过forwardInvocation:来寻求能够处理的对象.

    3.结论: 如此看来NSProxy确实更适合实现做为消息转发的代理类, 因为作为一个抽象类, NSProxy自身能够处理的方法极小(仅<NSObject>接口中定义的部分方法), 所以其它方法都能够按照设计的预期被转发到被代理的对象中.

    http://www.tanhao.me/code/160702.html/

  • 相关阅读:
    antd vue如何在父组件里打开子组件(子组件是个模态框)?
    webpack知识点整理
    JavaScript中常用的方法汇总,全部整理好了,一定要收藏!
    从地址栏里面获取参数的值
    何为垫片?垫片是一种什么概念在js中?
    async 与 await使用
    纯手撸Promise
    如何使用 Promise?
    Notification 浏览器桌面通知的使用
    学习骨架屏(Skeleton Screens)技术
  • 原文地址:https://www.cnblogs.com/feng9exe/p/10956565.html
Copyright © 2011-2022 走看看