一、单继承与多继承概念
继承是面向对象的基本特征之一,在具体语言的语法上设计有两种形式:多继承与单继承。
1.单继承
一个子类只有一个父类。
优点:类层次机结构清晰,设计上更容易把握。
缺点:在丰富度要求较高和较复杂的情况下,单继承从设计结构上无法满足。
2.多继承
一个子类可以有多个父类。
优点:由于同时具备多个父类的特征,让子类拥有更高的丰富度。
缺点:会让继承的结构变得更复杂,而且会出现菱形继承的风险。
Objective-C 不支持多继承,但我们可以间接的实现(协议、分类、消息转发)
二、实现多继承的几种方式
1.组合
DJTestClassC 继承 DJTestClassA 和 DJTestClassB 的方法,具体实现如下:
DJTestClassA:
#import <Foundation/Foundation.h>
@interface DJTestClassA : NSObject
-(void)classAMethod;
@end
#import "DJTestClassA.h"
@implementation DJTestClassA
-(void)classAMethod{
NSLog(@"classAMethod");
}
@end
DJTestClassB:
#import <Foundation/Foundation.h>
@interface DJTestClassB : NSObject
-(void)classBMethod;
@end
#import "DJTestClassB.h"
@implementation DJTestClassB
-(void)classBMethod{
NSLog(@"classBMethod");
}
@end
DJTestClassC:
#import <Foundation/Foundation.h>
@interface DJTestClassC : NSObject
-(void)classCMethod;
@property (nonatomic,strong)DJTestClassA *classA;
@property (nonatomic,strong)DJTestClassB *classB;
@end
#import "DJTestClassC.h"
#import "DJTestClassA.h"
#import "DJTestClassB.h"
@implementation DJTestClassC
-(instancetype)init{
if (self = [super init]) {
self.classA = [[DJTestClassA alloc]init];
self.classB = [[DJTestClassB alloc]init];
}
return self;
}
-(void)classCMethod{
[self.classA classAMethod];
[self.classB classBMethod];
}
@end
2.协议
一个类可以遵守多个协议,需要实现多个协议的方法,以此来达到多继承的效果。概念上的多继承和多继承应该是继承父类的属性和方法,并且不需要重写即可使用,通过协议实现多继承有以下不同:
- 子类需要实现协议方法
- 由于协议无法定义属性(只是声明
setter/getter
方法),所以该方法只能实现方法的多继承。
DJTestClassC 类继承 DJProtocolA 和 DJProtocolB 协议方法并实现,具体实现如下:
DJProtocolA:
#import <Foundation/Foundation.h>
@protocol DJProtocolA <NSObject>
-(void)protocolMethodA;
@end
DJProtocolB:
#import <Foundation/Foundation.h>
@protocol DJProtocolB <NSObject>
-(void)protocolMethodB;
@end
DJTestClassC:
#import <Foundation/Foundation.h>
#import "DJProtocolA.h"
@interface DJTestClassC : NSObject<DJProtocolA>//DJProtocolA公有方法,外部可调用
@end
#import "DJTestClassC.h"
#import "DJProtocolB.h"
@interface DJTestClassC ()<DJProtocolB>//DJProtocolB私有方法
@end
@implementation DJTestClassC
-(void)protocolMethodA{
NSLog(@"protocolMethodA");
}
-(void)protocolMethodB{
NSLog(@"protocolMethodB");
}
@end
3.类别
相对于协议,分类方法有一定的优势:
- 可以为分类添加方法。
- 可以为分类添加实例(通过 runtime 的关联属性),这是协议做不到的。
- 分类方便管理。
用分类实现上述协议实现的功能外再添加一个属性,创建一个 DJTestClassC 的分类,如下:
#import "DJTestClassC.h"
@interface DJTestClassC (extension)
// 声明属性
@property (nonatomic, copy) NSString *string;
@end
#import "DJTestClassC+extension.h"
#import <objc/runtime.h>
@implementation DJTestClassC (extension)
// 为分类添加属性
static const char *KString = "string";
- (NSString *)string {
return objc_getAssociatedObject(self, KString);
}
- (void)setString:(NSString *)string {
objc_setAssociatedObject(self, KString, string, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
//分类重写方法会覆盖原类方法
-(void)protocolMethodA{
NSLog(@"protocolMethodA--extension");
}
-(void)protocolMethodB{
NSLog(@"protocolMethodB--extension");
}
@end
4.消息转发
(1)快速转发
#import <Foundation/Foundation.h>
@interface DJTestClassC : NSObject
@end
#import "DJTestClassC.h"
#import "DJTestClassA.h"
#import "DJTestClassB.h"
@interface DJTestClassC ()
@end
@implementation DJTestClassC
- (id)forwardingTargetForSelector:(SEL)aSelector {
DJTestClassA *classA = [[DJTestClassA alloc] init];
DJTestClassB *classB = [[DJTestClassB alloc] init];
if ([classA respondsToSelector:aSelector]) {
return classA; // 转发给 classA 对象
}
else if ([classB respondsToSelector:aSelector]) {
return classB; // 转发给 classB 对象
}
return nil;
}
@end
调用的时候:
DJTestClassC *c = [[DJTestClassC alloc]init];
//1.在performSelector中使用NSSelectorFromString会造成警告,可以通过设置不检测performSelector内存泄露关闭警告
[c performSelector:NSSelectorFromString(@"classAMethod")];
//2.类型强转
[(DJTestClassB *)c classBMethod];
通过消息的快速转发,实现了动态性,真正的将方法交给其他类来实现,而非协议或者分类所需要自行实现。同时,消息转发也给我们了充分的灵活性,在不暴露这些接口的情况下通过类型强转来调用。
(2)慢速转发
慢速转发由程序员控制转发的过程,同时也可以实现对多个对象的转发,快速转发只能把该方法直接转发给其他某对象。
#import "DJTestClassC.h"
#import "DJTestClassA.h"
#import "DJTestClassB.h"
@interface DJTestClassC ()
{
// 创建成员实例
DJTestClassA *_classA;
DJTestClassB *_classB;
}
@end
@implementation DJTestClassC
-(instancetype)init{
self = [super init];
if (self) {
_classA = [[DJTestClassA alloc]init];
_classB = [[DJTestClassB alloc]init];
}
return self;
}
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
// 尝试自行实现方法签名
NSMethodSignature *methodSignature = [super methodSignatureForSelector:aSelector];
if (methodSignature == nil) { // 若无法实现,尝试通过多继承得到的方法实现
// 判断方法是哪个父类的,通过其创建方法签名
if ([_classA respondsToSelector:aSelector]) {
methodSignature = [_classA methodSignatureForSelector:aSelector];
}
else if ([_classB respondsToSelector:aSelector]) {
methodSignature = [_classB methodSignatureForSelector:aSelector];
}
}
return methodSignature;
}
// 为方法签名后,转发消息
- (void)forwardInvocation:(NSInvocation *)anInvocation {
SEL sel = [anInvocation selector];
// 判断哪个类实现了该方法
if ([_classA respondsToSelector:sel]) {
[anInvocation invokeWithTarget:_classA];
}
else if ([_classB respondsToSelector:sel]) {
[anInvocation invokeWithTarget:_classB];
}
}
@end