经常发现在一些需要使用消息转发而创建代理类时, 不同的程序员都有着不同的使用方法, 有些采用继承于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>接口中定义的部分方法), 所以其它方法都能够按照设计的预期被转发到被代理的对象中.

 

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

  1. 使用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. Android启动icon切图大小

    我们在给app切图的时候不知道告诉ui启动图标切多大,其实你新建一个android项目就知道应该切多大了.新建一个项目会产生几种大小的启动图标. 我把各个文件夹下的ic_launcher.png文件都 ...

  2. scikit-learn 朴素贝叶斯类库使用小结

    之前在朴素贝叶斯算法原理小结这篇文章中,对朴素贝叶斯分类算法的原理做了一个总结.这里我们就从实战的角度来看朴素贝叶斯类库.重点讲述scikit-learn 朴素贝叶斯类库的使用要点和参数选择. 1. ...

  3. Objective-C中小怪兽的逻辑

    学习Objective-C的面向对象也有一段时间了,为了犒劳自己的学习成果,写个小怪兽来犒劳一下自己把.在LOL中有怪兽和英雄的角色吧,接下来就先写一个小怪兽的类吧.从小怪兽的角度来讲,怪兽都有那些行 ...

  4. UCOSII内核代码分析

    1 UCOSII定义的关键数据结构 OS_EXT  INT8U             OSIntNesting; OSIntNesting用于判断当前系统是否正处于中断处理例程中. OS_EXT   ...

  5. Java多线程系列目录(共43篇)

    最近,在研究Java多线程的内容目录,将其内容逐步整理并发布. (一) 基础篇 01. Java多线程系列--“基础篇”01之 基本概念 02. Java多线程系列--“基础篇”02之 常用的实现多线 ...

  6. 总结JavaScript事件机制

    JavaScript事件模型 在各种浏览器中存在三种事件模型: 原始事件模型 , DOM2事件模型 , IE事件模型. 其中原始的事件模型被所有浏览器所支持,而DOM2中所定义的事件模型目前被除了IE ...

  7. 不要给<a>设置outline:none

    outline属性有什么作用 原文链接 a{outline:none} do not do it 当用户使用tab键进行链接切换时,该属性会在当前选中的链接(获得焦点)使用该属性,一般来说是虚线框 的 ...

  8. java操作数据库增删改查的小工具1--TxQueryRunner

    在java程序中,一般使用jdbc连接数据库,比较麻烦,在看传智教程时学了一个工具类,用于简化与数据库之间的操作步骤,就是TxQueryRunner,他是QueryRunner的子类,用起来和他是一样 ...

  9. 2.羽翼sqlmap学习笔记之MySQL注入

    1.判断一个url是否存在注入点: .sqlmap.py -u "http://abcd****efg.asp?id=7" -dbs 假设找到数据库:student ------- ...

  10. Dapper ORM VS SqlSugar ORM的 8场对决

    CUP和.NET SQL版本不同也会存在少许差距,但不会有质变,下面的测试结果仅供参考 比赛规则 1.统一使用Realse版本的最新 DLL,Realse模式启用程序 2.为了平衡CPU和数据库空闲情 ...