前言
OC
中类是不支持多继承的,一个类只有一个父类, 这就是单一继承,但是我们可以用协议protocol
和 NSProxy
实现多继承。
(1)先说协议protocol
,协议是我们用的最多的地方,就是代理,其实代理不叫代理,叫委托,这里就不多说了,相信大家都很熟了。
(2)因为基于运行时的机制,所以可以使用NSProxy
类让它来实现一下"伪多继承"。
什么是NSProxy
NSObject
类是Objective-C
中大部分类的基类,但是NSProxy
是和NSObject
同级的一个类,可以说它是一个虚拟类。
NS_ROOT_CLASS
@interface NSProxy <NSObject> {
Class isa;
}
可以看到,它遵守了 <NSObject>
协议,并且第一个Ivar
是一个isa
指针,因此它完全是可以拿来当一个 NSObject
或其派生类来使用的。
查看Foundation/NSObject.h
发现NSProxy
没有init
初始化方法,只有+ (id)alloc;
方法,也就是说如果我们要获得一个NSProxy
的实例只能通过alloc
方法。NSProxy
的使用也非常简单,通常,你只需要实现两个方法:
- (void)forwardInvocation:(NSInvocation *)invocation;
- (nullable NSMethodSignature *)methodSignatureForSelector:(SEL)sel;
NSProxy 与 NSObject 的消息传递的不同:
NSObject
消息传递:
NSObject
收到消息会先去缓存列表查找SEL
,若是找不到,就到自身方法列表中查找,依然找不到就顺着继承链进行查找,依然找不到的话,那就是unknown selector
,进入消息转发程序:
1. +(BOOL)resolveInstanceMethod:
其返回值为BOOL
类型,表示这个类是否通过class_addMethod
新增一个实例方法用以处理该 unknown selector
,也就是说在这里可以新增一个处理 unknown selector
的方法,若不能,则继续往下传递(若 unknown selector
是类方法,那么调用 +(BOOL)resolveClassMethod:
)
2. - (id)forwardingTargetForSelector:
这是第二次机会进行处理 unknown selector
,即转移给一个能处理 unknown selector
的其它对象,若返回一个其它的执行对象,那消息从 id objc_msgSend ( id self, SEL op, ...)
重新开始,若不能,则返回 nil
,并继续向下传递,最后的一次消息处理机会(3 与 4 配套)
3. - (NSMethodSignature *)methodSignatureForSelector:
返回携带参数类型、返回值类型和长度等的 selector
签名信息 NSMethodSignature
对象,Runtime
内部会基于 NSMethodSignature
实例构建一个NSInvocation
对象,作为回调- (void)forwardInvocation:
的入参
4. - (void)forwardInvocation:
这一步可以对传进来的 NSInvocation
进行一些操作,它主要在对象之间或者应用程序之间存储和转发消息(命令模式的实现),灵活性很高,譬如修改 target
、参数、甚至返回值,有兴趣可以去了解下NSInvocation
NSProxy
消息传递:
对于NSProxy
就没有这么复杂了,接收到 unknown selector
后,直接回调- (NSMethodSignature *)methodSignatureForSelector:
和- (void)forwardInvocation:
,就是上面的3和4的步骤,消息转发过程简单的很多。
NSProxy的使用
使用上也很简单,就是将具体接收消息的实际对象保存在容器中,并按顺序让它们接收消息即可
第一步:
/**
NSProxy 的第一步,也是NSObject消息转发的最后一次机会
对于 NSProxy 未实现立马走这里
消息获得函数的参数和返回值类型,即返回一个函数签名
@param sel selector 方法选择子
@return NSMethodSignature 函数签名
返回nil,Runtime 则会发出 doesNotRecognizeSelector: 消息,程序 crash
返回了NSMethodSignature,Runtime 就会创建一个 NSInvocation 对象并发送 -forwardInvocation: 消息给目标对象进入下一步
*/
- (nullable NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
if ([self.target respondsToSelector:sel]) {
return [self.target methodSignatureForSelector:sel];
} else {
return [super methodSignatureForSelector:sel];
}
}
第二步:
/**
可以在 forwardInvocation: 里修改传进来的 NSInvocation 对象,然后发送 invokeWithTarget: 消息给它,传进去一新的目标执行
@param invocation 对一个消息的描述,包括 selector 以及参数等信息
*/
- (void)forwardInvocation:(NSInvocation *)invocation {
// 拿到这个消息
SEL sel = invocation.selector;
if ([self.target respondsToSelector:sel]) {
// 调用这个对象,进行转发
[invocation invokeWithTarget:self.target];
}else {
[super forwardInvocation:invocation];
}
}
具体的可以去看