经常发现在一些需要使用消息转发而创建代理类时, 不同的程序员都有着不同的使用方法, 有些采用继承于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/

使用NSProxy和NSObject设计代理类的差异的更多相关文章

  1. iOS Class 使用NSProxy和NSObject设计代理类的差异

    经常发现在一些需要使用消息转发而创建代理类时, 不同的程序员都有着不同的使用方法, 有些采用继承于NSObject, 而有一些采用继承自NSProxy. 二者都是Foundation框架中的基类, 并 ...

  2. 【C++沉思录】代理类

    1.考虑下面的场景:设计一个容器,包含一组类型不同但相互关联的对象(比如:Animal,Dog,Cat),对象具备多态行为.2.容器一般只能包含一种类型的对象,使用vector<Animal&g ...

  3. Java通过代理类实现数据库DAO操作

    下面的所有代码示例都取自李兴华的<Java Web开发实战经典>的随书源码,因为觉得设计得很好,所以将代码摘录下来作成笔记. 首先,我们在一个java文件中定义要存储的结构类型: impo ...

  4. CGLIB 和 JDK生成动态代理类的区别(转)

    文章转自http://luyuanliang.iteye.com/blog/1137292 AOP 使用的设计模式就是代理模式,是对IOC设计的补充.为了扩展性,往往会加上反射,动态生成字节码,生成代 ...

  5. c++ 沉思录---代理类

    一.问题 如何设计一容器能包含彼此不同而又相互关联的类的对象(处于完整的继承层次的类)?因为一般的数组容器都只能包含一种类型的对象. 假设有一个表示不同类型的交通工具的类的派生层次: class Ve ...

  6. .Net基础——程序集与CIL HttpClient封装方法 .Net Core 编码规范 C#中invoke和beginInvoke的使用 WebServeice 动态代理类

    .Net基础——程序集与CIL   1. 程序集和CIL: 程序集是由.NET语言的编译器接受源代码文件产生的输出文件,通常分为 exe和dll两类,其中exe包含Main入口方法可以双击执行,dll ...

  7. c++中代理类的学习

    https://blog.csdn.net/lcg910978041/article/details/51468680 C++代理类是为了解决这样的问题: 容器通常只能包含一种类型的对象,所以很难在容 ...

  8. 面试官:你说你懂动态代理,那你知道为什么JDK中的代理类都要继承Proxy吗?

    之前我已经写过了关于动态代理的两篇文章,本来以为这块应该没啥问题,没想到今天又被难住了- 太难了!!! 之前文章的链接: 动态代理学习(一)自己动手模拟JDK动态代理. 动态代理学习(二)JDK动态代 ...

  9. 重学 Java 设计模式:实战代理模式「模拟mybatis-spring中定义DAO接口,使用代理类方式操作数据库原理实现场景」

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 难以跨越的瓶颈期,把你拿捏滴死死的! 编程开发学习过程中遇到的瓶颈期,往往是由于看不 ...

随机推荐

  1. ELK学习笔记之Kibana查询和使用说明

    0x00 概述 elasticsearch构建在Lucene之上,过滤器语法和Lucene相同 Kibana接口概述 Kibana接口分为四个主要部分: 发现 可视化 仪表板 设置 我们将按照列出的顺 ...

  2. @Autowired注解到底是byType还是byName?

    2016-08-05 14:29:32 杨家昌 阅读数 13400更多 分类专栏: spring   版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明 ...

  3. apache设置外网访问的方法 (服务器)

    环境:windows server 2012 R2 apache 2.4 首先,找到apache配置文件httpd.conf,找到配置: 代码展示: <directory /> optio ...

  4. MySql安装学习笔记

    参考地址:http://www.cnblogs.com/laumians-notes/p/9069498.html 该文章仅作为自己的学习笔记 一.准备mysql-8.0.11-winx64压缩包(示 ...

  5. Java正则表达式校验QQ号码和邮箱地址格式

    public class Demo02 { public static void main(String[] args) { Scanner sc = new Scanner(System.in); ...

  6. 8 Traits of an Experienced Programmer that every beginner programmer should know

    Referrence: http://whats-online.info/guides-and-info/36/Traits-of-Experienced-Programmer-that-every- ...

  7. element-ui 上传图片或视频时,先回显在上传

    <el-upload class="upload-demo" ref="vidos" :action="URL+'/api/post/file' ...

  8. js中函数的参数为函数的情况即回调函数

    js中函数的参数可以是数组对象也可以是函数,当参数为函数时我们叫做回调函数 //定义回调函数function B() { console.log("函数B")setTimeout( ...

  9. 4 Android可执行文件

    APK是Android Package缩写,使用zip解压文件即可打开.每个APK文件中都包含一个class.dex文件(odex过的APK文件除外).class.dex文件就是Android系统Da ...

  10. git 自定义log

    个人记录防止忘记 log别名: git config --global alias.lg=log --color --graph --pretty=format:'%Cred%h%Creset -%C ...