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

    转自:https://www.cnblogs.com/zsb517/p/5724908.html Unity3D 里有两种动态加载机制:一个是Resources.Load,另外一个通过AssetBun ...

  2. 第十一节:Asp.Net Core 之内容缓存(IMemoryCache)

    1. 整体说明 ASP.NET Core 支持多种不同的缓存,最简单的缓存基于 IMemoryCache,它表示存储在 Web 服务器内存中的缓存,内存缓存可以存储任何对象,存储形式键值对,需要 .n ...

  3. cmake 配置

    罗列一下cmake常用的命令.CMake支持大写.小写.混合大小写的命令. 1. 添加头文件目录INCLUDE_DIRECTORIES 语法:include_directories([AFTER|BE ...

  4. 关于 Windows to go

    1. 在宿主计算器的操作系统中访问 Windows to go 的磁盘 如题,如果需要在宿主计算器的操作系统中访问 Windows to go 的U盘(移动硬盘)中的文件,只需要打开磁盘管理,“更改驱 ...

  5. Kubernetes学习续之一键部署kubeadm

    1.Kubernetes的架构和组件,在部署时,它的每一个组件都是一个需要被执行的.单独的二进制文件,所以不难想象,SaltStack这样的运维工具或由社区维护的脚本的功能,就是要把这些二进制文件传输 ...

  6. ASP.NET Core中如何显示[PII is hidden]的隐藏信息

    有时候我们在ASP.NET Core项目运行时,发生在后台程序中的错误会将关键信息隐藏为[PII is hidden]这种占位符,如下所示: 而知道这些关键信息,有时候对我们调试程序是非常重要的.所以 ...

  7. DotnetSpider爬虫简单示例 net core

    文章地址 https://blog.csdn.net/sD7O95O/article/details/78097556 安装爬虫框架  NUGET 安装DotnetSpider 创建HTTP协议数据包 ...

  8. .NET CORE webapi epplus 导入导出 (实习第一个月的笔记)

    最近有个需求就是网页表格里面的数据导出到excel  于是从各位前辈的博客园搜了搜demo  大部分非为两类 都是用的插件NPOI和Eppluse ,因此在这里就介绍Eppluse 用法,还有就是在博 ...

  9. Vert.x Web

    https://vertx.io/docs/vertx-web/java/ Vert.x-Web是一组用于使用Vert.x构建Web应用程序的构建块.将其视为瑞士军刀,用于构建现代,可扩展的网络应用程 ...

  10. 如何在pycharm中设置环境变量

    今天运行tensorflow的时候,发现在pycharm下,程序无法找到CUDA的libcupti.so文件.而在添加完环境变量: export LD_LIBRARY_PATH=$LD_LIBRARY ...