OC中是不支持多继承的,但是某些情况下想实现类似多继承的效果怎么办呢?简单的总结了几种实现伪多继承的方法。不对之处欢迎大家指正!
第一种:组合
比如鱼类会游泳,鸟类会飞,现在有个怪物类,要求既会飞又会游泳该怎么办?就是在怪物类里各持有一个鱼类和鸟类的实例,外界调用怪物的飞行方法实际是其持有的鸟类实例调用鸟类飞行的方法,游泳方法亦是如此。直接上代码吧!
//Fish.h的代码
#import <Foundation/Foundation.h>
@interface Fish : NSObject
//游泳
- (void)swim;
@end
#import "Fish.h"
//Fish.m的代码
@implementation Fish
- (void)swim {
NSLog(@"开始游泳.");
}
@end
//Bird.h的代码
#import <Foundation/Foundation.h>
@interface Bird : NSObject
//飞行
- (void)fly;
@end
//Bird.m的代码
#import "Bird.h"
@implementation Bird
- (void)fly {
NSLog(@"开始起飞.");
}
@end
//Monster.h的代码
#import <Foundation/Foundation.h>
@interface Monster : NSObject
//游泳
- (void)swim;
//飞行
- (void)fly;
@end
//Monster.m的代码
#import "Monster.h"
#import "Fish.h"
#import "Bird.h"
@interface Monster ()
@property (nonatomic, strong) Fish *fish;
@property (nonatomic, strong) Bird *bird;
@end
@implementation Monster
- (instancetype)init {
if (self = [super init]) {
self.fish = [Fish new];
self.bird = [Bird new];
}
return self;
}
- (void)swim {
[_fish swim];
}
- (void)fly {
[_bird fly];
}
这样创建Monster实例对象就既可以调用游泳方法又可以调用飞行方法了。
//组合
Monster *m = [Monster new];
[m swim];
[m fly];
第二种:Protocol
使用Protocol来实现多继承,因为Protocol只能提供接口,而没有提供实现方式,所以这种方法只能实现接口多继承。如果只是想多继承基类的接口,那么遵守多协议无疑是最好的方法,而既需要多继承接口,又要多继承其实现,那么协议是无能为力了。
直接上代码。
//Add.h的代码
#ifndef Add_h
#define Add_h
@protocol Add <NSObject>
- (int)addA:(int)a b:(int)b;
@end
#endif /* Add_h */
//Sub.h的代码
#ifndef Sub_h
#define Sub_h
@protocol Sub <NSObject>
- (int)subA:(int)a b:(int)b;
@end
#endif /* Sub_h */
//Mul.h的代码
#ifndef Mul_h
#define Mul_h
@protocol Mul <NSObject>
- (int)mulA:(int)a b:(int)b;
@end
#endif /* Mul_h */
//Calculator.h的代码
#import <Foundation/Foundation.h>
#import "Add.h"
#import "Sub.h"
#import "Mul.h"
NS_ASSUME_NONNULL_BEGIN
@interface Calculator : NSObject<Add,Sub,Mul>
@end
NS_ASSUME_NONNULL_END
//Calculator.m的代码
#import "Calculator.h"
@implementation Calculator
#pragma mark - Add
- (int)addA:(int)a b:(int)b {
return a+b;
}
#pragma mark - Sub
- (int)subA:(int)a b:(int)b {
return a-b;
}
#pragma mark - Mul
- (int)mulA:(int)a b:(int)b {
return a*b;
}
@end
这样创建Calculator实例对象就可以调用Add、Sub和Mul协议里面方法了。从而实现接口多继承。但是只遵循协议相当于声明了协议里的方法,不实现的话运行时调用会报错unrecognized selector sent to instance xxxxxx.
//protocol
int a=8,b=4;
Calculator *c = [Calculator new];
NSLog(@"%d+%d=%d",a,b,[c addA:a b:b]);
NSLog(@"%d-%d=%d",a,b,[c subA:a b:b]);
NSLog(@"%dx%d=%d",a,b,[c mulA:a b:b]);
第三种:组合+Protocol
下面模拟一个卖书的,一个卖衣服的,还有一个既卖书又卖衣服的经销商。这种方法实际是使用runtime和Protocol对第一种方法的延伸和扩展。直接上代码。
//BookProvider.h的代码
#import <Foundation/Foundation.h>
@protocol BookProviderDelegate <NSObject>
- (void)sellBookWithTitle:(NSString *)title;
@end
//在本类里下面三种方式都可以,效果也不尽相同(可以自己进行尝试):
//第一种:不遵循BookProviderDelegate,也不是对相关方法进行声明。这种情况自己本身实例不能进行相关方法调用
//第二种:遵循BookProviderDelegate,这种情况自己本身实例也可以进行相关方法调用
//第三种:相关方法进行声明,这种情况自己本身实例也可以进行相关方法调用
@interface BookProvider : NSObject<BookProviderDelegate>//遵循代理
//- (void)sellBookWithTitle:(NSString *)title;
//声明不实现不会在methodlist中找到,所以调用会报错 unrecognized selector sent to instance
//methodlist中都是.m中有实现的方法
- (void)publicMethod;
@end
//BookProvider.m的代码
#import "BookProvider.h"
@implementation BookProvider
#pragma mark - BookProviderDelegate
- (void)sellBookWithTitle:(NSString *)title {
NSLog(@"You've bought \"%@\".",title);
}
#pragma mark - 私有方法
- (void)packageBook {
}
@end
//ClothProvider.h的代码
#import <Foundation/Foundation.h>
typedef enum : NSUInteger {
ClothesSizeSmall = 0,
ClothesSizeMedium,
ClothesSizeLarge,
} ClothSize;
@protocol ClothProviderDelegate <NSObject>
- (void)sellClothWithSize:(ClothSize)size;
@end
@interface ClothProvider : NSObject<ClothProviderDelegate>
//- (void)sellClothWithSize:(ClothSize)size;
@end
//ClothProvider.m的代码
#import "ClothProvider.h"
@implementation ClothProvider
#pragma mark - ClothProviderDelegate
- (void)sellClothWithSize:(ClothSize)size {
NSString *sizeStr;
switch (size) {
case ClothesSizeLarge:
sizeStr = @"large size";
break;
case ClothesSizeMedium:
sizeStr = @"medium size";
break;
case ClothesSizeSmall:
sizeStr = @"small size";
break;
default:
break;
}
NSLog(@"You've bought some clothes of %@.",sizeStr);
}
@end
//DealerProxy.h的代码
#import <Foundation/Foundation.h>
#import "BookProvider.h"
#import "ClothProvider.h"
@interface DealerProxy : NSProxy<BookProviderDelegate,ClothProviderDelegate>
+ (instancetype )dealerProxy;
@end
//DealerProxy.h的代码
#import "DealerProxy.h"
#import <objc/runtime.h>
@interface DealerProxy (){
BookProvider *_bookProvider;
ClothProvider *_clothProvider;
//方法map
NSMutableDictionary *_methodsMap;
}
@end
@implementation DealerProxy
+ (instancetype)dealerProxy {
return [[DealerProxy alloc] init];
}
- (instancetype)init {
_bookProvider = [BookProvider new];
_clothProvider = [ClothProvider new];
_methodsMap = [NSMutableDictionary dictionary];
//获取method并存入_methodsMap中
[self _registerMethodsWithTarget:_bookProvider];
[self _registerMethodsWithTarget:_clothProvider];
return self;
}
#pragma mark - private method
- (void)_registerMethodsWithTarget:(id)target {
unsigned int numberOfMethods = 0;
//这里处理的只是针对对象方法,类方法没有处理.注意:这里的方法列表只包含.m中实现的方法,只声明的方法不包括在内。
Method *method_list = class_copyMethodList([target class], &numberOfMethods);
for (int i = 0; i < numberOfMethods; i++) {
Method method = method_list[i];
SEL sel = method_getName(method);
NSString *selName = NSStringFromSelector(sel);
[_methodsMap setObject:target forKey:selName];
}
NSLog(@"-->%@",_methodsMap);
free(method_list);
}
#pragma mark - NSProxy override methods
//消息转发流程
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
NSString *selName = NSStringFromSelector(sel);
id target = _methodsMap[selName];
if (target && [target respondsToSelector:sel]) {
return [target methodSignatureForSelector:sel];
}else{
return [super methodSignatureForSelector:sel];
}
}
//消息转发流程
- (void)forwardInvocation:(NSInvocation *)invocation {
SEL sel = invocation.selector;
NSString *selName = NSStringFromSelector(sel);
id target = _methodsMap[selName];
if (target && [target respondsToSelector:sel]) {
[invocation invokeWithTarget:target];
}else{
[super forwardInvocation:invocation];
}
}
@end
这样DealerProxy实例既可以卖书又可以卖衣服了。其中涉及到NSProxy、消息转发以及runtime相关知识大家请自行百度。
//组合+Protocol
BookProvider *b = [BookProvider new];
[b sellBookWithTitle:@"<<从删库到跑路>>"];
ClothProvider *c = [ClothProvider new];
[c sellClothWithSize:ClothesSizeLarge];
DealerProxy *p = [DealerProxy dealerProxy];
[p sellBookWithTitle:@"Object C"];
[p sellClothWithSize:ClothesSizeMedium];