使用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/
使用NSProxy和NSObject设计代理类的差异的更多相关文章
- iOS Class 使用NSProxy和NSObject设计代理类的差异
经常发现在一些需要使用消息转发而创建代理类时, 不同的程序员都有着不同的使用方法, 有些采用继承于NSObject, 而有一些采用继承自NSProxy. 二者都是Foundation框架中的基类, 并 ...
- 【C++沉思录】代理类
1.考虑下面的场景:设计一个容器,包含一组类型不同但相互关联的对象(比如:Animal,Dog,Cat),对象具备多态行为.2.容器一般只能包含一种类型的对象,使用vector<Animal&g ...
- Java通过代理类实现数据库DAO操作
下面的所有代码示例都取自李兴华的<Java Web开发实战经典>的随书源码,因为觉得设计得很好,所以将代码摘录下来作成笔记. 首先,我们在一个java文件中定义要存储的结构类型: impo ...
- CGLIB 和 JDK生成动态代理类的区别(转)
文章转自http://luyuanliang.iteye.com/blog/1137292 AOP 使用的设计模式就是代理模式,是对IOC设计的补充.为了扩展性,往往会加上反射,动态生成字节码,生成代 ...
- c++ 沉思录---代理类
一.问题 如何设计一容器能包含彼此不同而又相互关联的类的对象(处于完整的继承层次的类)?因为一般的数组容器都只能包含一种类型的对象. 假设有一个表示不同类型的交通工具的类的派生层次: class Ve ...
- .Net基础——程序集与CIL HttpClient封装方法 .Net Core 编码规范 C#中invoke和beginInvoke的使用 WebServeice 动态代理类
.Net基础——程序集与CIL 1. 程序集和CIL: 程序集是由.NET语言的编译器接受源代码文件产生的输出文件,通常分为 exe和dll两类,其中exe包含Main入口方法可以双击执行,dll ...
- c++中代理类的学习
https://blog.csdn.net/lcg910978041/article/details/51468680 C++代理类是为了解决这样的问题: 容器通常只能包含一种类型的对象,所以很难在容 ...
- 面试官:你说你懂动态代理,那你知道为什么JDK中的代理类都要继承Proxy吗?
之前我已经写过了关于动态代理的两篇文章,本来以为这块应该没啥问题,没想到今天又被难住了- 太难了!!! 之前文章的链接: 动态代理学习(一)自己动手模拟JDK动态代理. 动态代理学习(二)JDK动态代 ...
- 重学 Java 设计模式:实战代理模式「模拟mybatis-spring中定义DAO接口,使用代理类方式操作数据库原理实现场景」
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 难以跨越的瓶颈期,把你拿捏滴死死的! 编程开发学习过程中遇到的瓶颈期,往往是由于看不 ...
随机推荐
- [工具]法国神器mimikatz 2.1.1 一键版 & PowerShell版
无需任何参数,运行EXE即可自动读取Windows系统密码 EXE版需要其它功能请使用原版 (参数已写死仅读密码) 结果保存于当前目录mz.log EXE https://github.com/k8g ...
- go中&^(按位置零)符号的含义
go中有一个 &^ 的运算符,它代表的是按位置零 首先来看下几个输出例子: i := 1 &^ 0 fmt.Println("1 &^ 0 -- ",i) ...
- spring boot EnableAutoConfiguration exclude 无效
本文链接:https://blog.csdn.net/ID19870510/article/details/79373386 首先讲一下SpringBootApplication注解源码定义为 @Ta ...
- C# 调用Access数据库关于like模糊查询的写法
在access查询视图中要使用"*"做模糊匹配,但是在程序中要用%来匹配.在access中:NEIBUBH like '*1234*'在程序中:NEIBUBH like '%123 ...
- Netty高性能原理和框架架构解析
1.引言 Netty 是一个广受欢迎的异步事件驱动的Java开源网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端. 本文基于 Netty 4.1 展开介绍相关理论模型,使用场景,基本组件 ...
- 详细的Hadoop的入门教程-完全分布模式Fully-Distributed Operation
1. 前面在伪分布模式下已经创建了一台机器,为了统一命名,hostname更名为hadoop01.然后再克隆2台机器:hadoop02. hadoop03:将第一台机器hadoop01上的伪分布停止, ...
- pandas-18 reindex用法
pandas-18 reindex用法 pandas中的reindex方法可以为series和dataframe添加或者删除索引. 方法:serise.reindex().dataframe.rein ...
- 学习笔记之Django
The Web framework for perfectionists with deadlines | Django https://www.djangoproject.com/ Django m ...
- mockjs的基本使用入门
相信很多前端同学都有一个困扰,就是没有后端数据的情况下感觉很多想法都不能动手去实现,这里介绍一个模拟后端数据的工具,可以一定程度上解决我们的困扰. 很多人或多或少的都听说过mockjs,都知道是一个模 ...
- 93.vue---在vue环境用webuploader分片上传插件遇到的超级bug(独家仅此一份)
本来我是想想用vue-simple-uploader (https://www.cnblogs.com/xiahj/p/vue-simple-uploader.html)的 但是公司后台已经做好了we ...