OC不像C++一样支持多重继承,当我们需要从多个父类汲取属性和方法时,需要用到一些特殊的操作。
1. 组合
在ClassC的.h或者.m文件中,声明ClassA和ClassB的实例
@class ClassA,ClassB;
@interface ClassC : NSObject
@property (nonatomic,strong) ClassA *a;
@property (nonatomic,strong) ClassB *b;
- (void)sport;
@end
#import "ClassC.h"
#import "ClassA.h"
#import "ClassB.h"
@implementation ClassC
- (void)sport{
[self.a run];
[self.b walk];
}
@end
2. 协议
将C类需要继承的方法以及属性在ClassA和ClassB中各自声明一份协议,C类遵守这两份协议,同时在C类中实现协议中的方法以及属性。
@protocol ClassADelegate
- (void)run;
@property (nonatomic,copy) NSString *runDistance;
@end
@protocol ClassBDelegate
- (void)walk;
@property (nonatomic,copy) NSString *walkDistance;
@end
#import "ClassC.h"
#import "ClassA.h"
#import "ClassB.h"
@interface ClassC()<ClassADelegate,ClassBDelegate>
@end
@implementation ClassC
- (void)sport{
[self run];
[self walk];
}
- (void)run{
self.runDistance = @"10000";
NSLog(@"今天跑了%@步",self.runDistance);
}
- (void)walk{
self.walkDistance = @"5000";
NSLog(@"今天走了%@步",self.walkDistance);
}
3. 消息转发
当向某个对象发送消息,但runtime system在当前类以及父类中都找不到对应方法的实现时,runtime system并不会立即报错使程序崩溃,而是依次执行下列步骤:
- 动态方法解析:向当前类发送
resolveInstanceMethod
: 信号,检查是否动态向该类添加了方法 - 快速消息转发:检查该类是否实现了
forwardingTargetForSelector:
方法,若实现了则调用这个方法,若该方法返回nil或者非self,则向该返回对象重新发送消息 - 标准消息转发:
runtime
发送methodSignatureForSelector:
消息获取Selector
对应的方法签名。返回值非空则通过forwardInvocation:
转发消息,返回值为空则向当前对象发送doesNotRecognizeSelector:
消息,程序崩溃退出。
例子:
/* ---------------------- Father.h ---------------------- */
#import <Foundation/Foundation.h>
@interface Father : NSObject
- (void)work;
@end
/* ---------------------- Father.m ---------------------- */
#import "Father.h"
@implementation Father
- (void)work {
NSLog(@"I'm working.");
}
@end
/* ---------------------- Mother.h ---------------------- */
#import <Foundation/Foundation.h>
@interface Mother : NSObject
- (void)cook;
@end
/* ---------------------- Mother.m ---------------------- */
#import "Mother.h"
@implementation Mother
- (void)cook {
NSLog(@"I'm cooking.");
}
@end
/* ---------------------- Son.m ---------------------- */
#import <Foundation/Foundation.h>
#import "Father.h"
@interface Son : Father
//利用OC的继承机制,这里单方面地先继承的Father类
@end
/* ---------------------- Son.m ---------------------- */
#import "Son.h"
@implementation Son
@end
3.1 快速转发
对应- (id)forwardingTargetForSelector:(SEL)aSelector;
方法,他是Apple提供了一种更为简便的方式进行消息转发。作用:对消息进行重定向。当一个对象无法处理消息时,系统会调用这个方法,可以利用这个方法将消息的接受者替换为其他对象。
/* ---------------------- Son.h ---------------------- */
#import "Mother.h"
...
/* ---------------------- Son.m ---------------------- */
#import "Son.h"
@interface Son()
@property (nonatomic, strong) Mother *mother;
@end
@implementation Son
- (instancetype)init {
self = [super init];
if (self != nil) {
_mother = [[Mother alloc]init];
}
return self;
}
- (id)forwardingTargetForSelector:(SEL)aSelector {
if ([_mother respondsToSelector:aSelector]) {
return _mother;
}
return nil;
}
@end
上面的代码中,当Son调用Cook方法时,无法处理,因此通过forwardingTargetForSelector
重定向给mother。
3.2 标准转发
我们还可以利用- (void)forwardInvocation:(NSInvocation *)anInvocation;
来进行标准的消息转发,值得注意的是,在调用这个方法之前,系统会先调(id)forwardingTargetForSelector:(SEL)aSelector;
这个方法,如果这个方法返回为nil,才会调动标准的消息转发机制。
--------------------- Son.m ---------------------- */
#import "Son.h"
@interface Son()
@property (nonatomic, strong) Mother *mother;
@end
@implementation Son
- (instancetype)init {
self = [super init];
if (self != nil) {
_mother = [[Mother alloc]init];
}
return self;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSMethodSignature *methodSignature = [super methodSignatureForSelector:aSelector];
if (methodSignature != nil) return methodSignature;
if ([_mother respondsToSelector:aSelector]) {
return [_mother methodSignatureForSelector:aSelector];
}
return nil;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
if ([_mother respondsToSelector:[anInvocation selector]]) {
[anInvocation invokeWithTarget:_mother];
}
}
@end
3.3 对比
快速转发和标准转发的区别:
- 快速消息转发:简单、快速、但仅能转发给一个对象。
- 标准消息转发:稍复杂、较慢、但转发操作实现可控,可以实现多对象转发。
为什么是快速转发是一对一,标准转发是一对多:
- 快速转发是return,只能返回一个对象。
- 标准转发是调用
[anInvocation invokeWithTarget:XXX];
能够反复调用多次。