MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Objective-C中的多继承模拟与协议多态性

2023-07-297.9k 阅读

Objective-C 中的多继承模拟

在传统的面向对象编程中,多继承是指一个类可以从多个父类中继承属性和方法。然而,C++ 等语言中多继承可能会带来诸如菱形继承问题(也称为“钻石问题”),即当一个类从多个父类继承相同的属性或方法时,可能会导致命名冲突和歧义。为了解决这些问题,Objective-C 并没有直接支持多继承,但提供了一些机制来模拟多继承的效果。

类别(Categories)

类别是 Objective-C 中一种为已有的类添加方法的方式,它允许在不创建子类的情况下,为现有的类增加新的功能。虽然类别不能添加实例变量,但可以添加方法,这在一定程度上模拟了多继承的部分特性。

下面是一个示例,假设有一个 NSString 类,我们使用类别为其添加一个新的方法:

// NSString+CustomAdditions.h
#import <Foundation/Foundation.h>

@interface NSString (CustomAdditions)
- (NSString *)reverseString;
@end

// NSString+CustomAdditions.m
#import "NSString+CustomAdditions.h"

@implementation NSString (CustomAdditions)
- (NSString *)reverseString {
    NSMutableString *reversedString = [NSMutableString string];
    for (NSUInteger i = self.length - 1; i != NSNotFound; i--) {
        [reversedString appendFormat:@"%C", [self characterAtIndex:i]];
    }
    return [NSString stringWithString:reversedString];
}
@end

在上述代码中,我们通过类别 NSString (CustomAdditions)NSString 类添加了一个 reverseString 方法,实现字符串反转的功能。使用时,任何 NSString 对象都可以调用这个新添加的方法:

#import <Foundation/Foundation.h>
#import "NSString+CustomAdditions.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSString *originalString = @"Hello, World!";
        NSString *reversedString = [originalString reverseString];
        NSLog(@"Reversed String: %@", reversedString);
    }
    return 0;
}

这里,类别就像是给 NSString 类混入了新的功能,类似于从另一个“父类”继承了 reverseString 方法,从而在一定程度上模拟了多继承。

委托(Delegation)

委托是一种设计模式,通过委托,一个对象(委托者)可以将某些任务交给另一个对象(代理)来完成。在 Objective-C 中,委托模式非常常见,它可以用来模拟多继承中行为的复用。

假设我们有一个视图控制器 ViewController,它需要处理一些复杂的网络请求逻辑。我们可以创建一个专门的网络请求代理类 NetworkRequestDelegate 来处理这些逻辑,然后让 ViewController 将网络请求相关的任务委托给 NetworkRequestDelegate

首先定义代理协议:

// NetworkRequestDelegateProtocol.h
#import <Foundation/Foundation.h>

@protocol NetworkRequestDelegateProtocol <NSObject>
- (void)performNetworkRequest;
@end

然后实现代理类:

// NetworkRequestDelegate.h
#import <Foundation/Foundation.h>
#import "NetworkRequestDelegateProtocol.h"

@interface NetworkRequestDelegate : NSObject <NetworkRequestDelegateProtocol>
@end

// NetworkRequestDelegate.m
#import "NetworkRequestDelegate.h"

@implementation NetworkRequestDelegate
- (void)performNetworkRequest {
    NSLog(@"Performing network request...");
    // 实际的网络请求代码
}
@end

接着在视图控制器中使用委托:

// ViewController.h
#import <UIKit/UIKit.h>
#import "NetworkRequestDelegateProtocol.h"

@interface ViewController : UIViewController
@property (nonatomic, strong) id<NetworkRequestDelegateProtocol> networkDelegate;
@end

// ViewController.m
#import "ViewController.h"

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    self.networkDelegate = [[NetworkRequestDelegate alloc] init];
    [self.networkDelegate performNetworkRequest];
}
@end

在上述代码中,ViewController 通过 networkDelegate 属性将网络请求任务委托给 NetworkRequestDelegate 类的实例。这就像是 ViewControllerNetworkRequestDelegate 类“继承”了网络请求相关的行为,模拟了多继承中行为的复用。

组合(Composition)

组合是一种将不同类的实例作为另一个类的属性的方式,通过这种方式,一个类可以利用其他类的功能。它是一种比继承更灵活的复用方式,也可以用于模拟多继承。

假设我们有一个 Car 类,它需要具备动力系统和导航系统的功能。我们可以创建 Engine 类和 NavigationSystem 类分别实现动力和导航功能,然后在 Car 类中通过组合来使用这两个类的功能。

定义 Engine 类:

// Engine.h
#import <Foundation/Foundation.h>

@interface Engine : NSObject
- (void)startEngine;
@end

// Engine.m
#import "Engine.h"

@implementation Engine
- (void)startEngine {
    NSLog(@"Engine started.");
}
@end

定义 NavigationSystem 类:

// NavigationSystem.h
#import <Foundation/Foundation.h>

@interface NavigationSystem : NSObject
- (void)navigateTo:(NSString *)destination;
@end

// NavigationSystem.m
#import "NavigationSystem.h"

@implementation NavigationSystem
- (void)navigateTo:(NSString *)destination {
    NSLog(@"Navigating to %@", destination);
}
@end

定义 Car 类:

// Car.h
#import <Foundation/Foundation.h>
#import "Engine.h"
#import "NavigationSystem.h"

@interface Car : NSObject
@property (nonatomic, strong) Engine *engine;
@property (nonatomic, strong) NavigationSystem *navigationSystem;
- (void)driveTo:(NSString *)destination;
@end

// Car.m
#import "Car.h"

@implementation Car
- (void)driveTo:(NSString *)destination {
    [self.engine startEngine];
    [self.navigationSystem navigateTo:destination];
    NSLog(@"Driving to %@", destination);
}
@end

Car 类中,通过组合 EngineNavigationSystem 类的实例,使其具备了启动引擎和导航的功能。这就像是 Car 类从 EngineNavigationSystem 类“继承”了相关功能,模拟了多继承。

协议多态性

协议是 Objective-C 中一种定义方法列表的方式,一个类可以声明遵循某个协议,从而保证实现协议中定义的方法。协议多态性指的是不同的类可以遵循同一个协议,并且以不同的方式实现协议中的方法,从而在运行时根据对象的实际类型调用不同的实现。

协议的定义与遵循

首先定义一个协议 ShapeProtocol,用于描述形状相关的方法:

// ShapeProtocol.h
#import <Foundation/Foundation.h>

@protocol ShapeProtocol <NSObject>
- (CGFloat)area;
- (CGFloat)perimeter;
@end

然后定义两个类 CircleRectangle,它们都遵循 ShapeProtocol 协议,并实现协议中的方法:

// Circle.h
#import <Foundation/Foundation.h>
#import "ShapeProtocol.h"

@interface Circle : NSObject <ShapeProtocol>
@property (nonatomic, assign) CGFloat radius;
@end

// Circle.m
#import "Circle.h"

@implementation Circle
- (CGFloat)area {
    return M_PI * self.radius * self.radius;
}
- (CGFloat)perimeter {
    return 2 * M_PI * self.radius;
}
@end
// Rectangle.h
#import <Foundation/Foundation.h>
#import "ShapeProtocol.h"

@interface Rectangle : NSObject <ShapeProtocol>
@property (nonatomic, assign) CGFloat width;
@property (nonatomic, assign) CGFloat height;
@end

// Rectangle.m
#import "Rectangle.h"

@implementation Rectangle
- (CGFloat)area {
    return self.width * self.height;
}
- (CGFloat)perimeter {
    return 2 * (self.width + self.height);
}
@end

在上述代码中,CircleRectangle 类都遵循了 ShapeProtocol 协议,并根据自身的特点实现了 areaperimeter 方法。

协议多态性的体现

接下来,我们可以创建一个函数,接受一个遵循 ShapeProtocol 协议的对象,并调用协议中的方法。这样,无论传入的是 Circle 还是 Rectangle 对象,都会根据对象的实际类型调用相应的实现。

void printShapeInfo(id<ShapeProtocol> shape) {
    NSLog(@"Area: %f", [shape area]);
    NSLog(@"Perimeter: %f", [shape perimeter]);
}

使用时:

#import <Foundation/Foundation.h>
#import "ShapeProtocol.h"
#import "Circle.h"
#import "Rectangle.h"

void printShapeInfo(id<ShapeProtocol> shape);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Circle *circle = [[Circle alloc] init];
        circle.radius = 5.0;
        printShapeInfo(circle);

        Rectangle *rectangle = [[Rectangle alloc] init];
        rectangle.width = 4.0;
        rectangle.height = 6.0;
        printShapeInfo(rectangle);
    }
    return 0;
}

printShapeInfo 函数中,虽然参数类型是 id<ShapeProtocol>,但在运行时,会根据传入对象的实际类型(CircleRectangle)调用相应的 areaperimeter 方法,这就是协议多态性的体现。

协议多态性与接口

在 Objective-C 中,协议类似于其他语言中的接口概念,但又有一些区别。协议只定义方法列表,不包含方法的实现,而类通过遵循协议来保证实现这些方法。这使得不同的类可以通过遵循相同的协议来实现类似的功能,从而实现多态性。

与传统接口不同的是,Objective-C 中的协议可以被任何类遵循,而不需要像某些语言那样强制继承某个特定的接口类。这种灵活性使得协议在 Objective-C 中被广泛应用于各种框架和设计模式中,例如委托模式中,委托者和代理之间通过协议来定义交互的方法。

协议的特性与应用场景

可选方法

协议中可以定义可选方法,这意味着遵循该协议的类不一定非要实现这些方法。在协议定义中,使用 @optional 关键字来标记可选方法。

例如,我们修改 ShapeProtocol 协议,添加一个可选方法 description

// ShapeProtocol.h
#import <Foundation/Foundation.h>

@protocol ShapeProtocol <NSObject>
- (CGFloat)area;
- (CGFloat)perimeter;
@optional
- (NSString *)description;
@end

这样,CircleRectangle 类可以选择实现 description 方法,也可以不实现。如果实现了,就可以提供关于形状的更详细描述。

// Circle.m
#import "Circle.h"

@implementation Circle
- (CGFloat)area {
    return M_PI * self.radius * self.radius;
}
- (CGFloat)perimeter {
    return 2 * M_PI * self.radius;
}
- (NSString *)description {
    return [NSString stringWithFormat:@"Circle with radius %f", self.radius];
}
@end
// Rectangle.m
#import "Rectangle.h"

@implementation Rectangle
- (CGFloat)area {
    return self.width * self.height;
}
- (CGFloat)perimeter {
    return 2 * (self.width + self.height);
}
@end

在使用时,我们需要检查对象是否实现了可选方法:

void printShapeInfo(id<ShapeProtocol> shape) {
    NSLog(@"Area: %f", [shape area]);
    NSLog(@"Perimeter: %f", [shape perimeter]);
    if ([shape respondsToSelector:@selector(description)]) {
        NSLog(@"Description: %@", [shape description]);
    }
}

这种可选方法的特性使得协议更加灵活,适用于不同的应用场景,类可以根据自身需求选择性地实现部分功能。

协议的继承

协议可以继承自其他协议,一个协议可以包含其继承的协议的所有方法。例如,我们可以定义一个新的协议 ColoredShapeProtocol,它继承自 ShapeProtocol 并添加了颜色相关的方法:

// ColoredShapeProtocol.h
#import <Foundation/Foundation.h>
#import "ShapeProtocol.h"

@protocol ColoredShapeProtocol <ShapeProtocol>
- (UIColor *)color;
@end

现在,任何遵循 ColoredShapeProtocol 协议的类都需要实现 ShapeProtocol 中的所有方法以及 ColoredShapeProtocol 中新增的 color 方法。

// ColoredCircle.h
#import <Foundation/Foundation.h>
#import "ColoredShapeProtocol.h"

@interface ColoredCircle : NSObject <ColoredShapeProtocol>
@property (nonatomic, assign) CGFloat radius;
@property (nonatomic, strong) UIColor *color;
@end

// ColoredCircle.m
#import "ColoredCircle.h"

@implementation ColoredCircle
- (CGFloat)area {
    return M_PI * self.radius * self.radius;
}
- (CGFloat)perimeter {
    return 2 * M_PI * self.radius;
}
- (UIColor *)color {
    return self.color;
}
@end

协议的继承使得我们可以在现有协议的基础上进行扩展,构建更复杂的协议层次结构,满足不同的业务需求。

协议在框架中的应用

在 iOS 开发框架中,协议被广泛应用。例如,UITableViewDataSourceUITableViewDelegate 协议是 UITableView 实现数据展示和用户交互的关键。UITableViewDataSource 协议定义了提供表格数据的方法,如 tableView:numberOfRowsInSection:tableView:cellForRowAtIndexPath:,而 UITableViewDelegate 协议定义了处理用户交互的方法,如 tableView:didSelectRowAtIndexPath:

一个视图控制器可以遵循这两个协议,实现协议中的方法来定制表格的展示和交互逻辑。这使得 UITableView 可以与不同的数据源和代理对象配合,实现多样化的表格展示效果,充分体现了协议多态性在框架开发中的应用。

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
@property (nonatomic, strong) NSArray *dataArray;
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    self.dataArray = @[@"Item 1", @"Item 2", @"Item 3"];

    UITableView *tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
    tableView.dataSource = self;
    tableView.delegate = self;
    [self.view addSubview:tableView];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.dataArray.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
    cell.textLabel.text = self.dataArray[indexPath.row];
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"Selected row: %ld", (long)indexPath.row);
}
@end

在上述代码中,ViewController 遵循了 UITableViewDataSourceUITableViewDelegate 协议,通过实现协议中的方法,为 UITableView 提供了数据和交互逻辑。不同的视图控制器可以通过遵循相同的协议,但实现不同的方法,来定制出不同展示和交互效果的表格,这就是协议多态性在实际框架应用中的体现。

模拟多继承与协议多态性的综合应用

在实际的项目开发中,我们常常会结合模拟多继承的方式和协议多态性来构建复杂而灵活的软件架构。

假设我们正在开发一个游戏,游戏中有各种角色,如战士(Warrior)、法师(Mage)和盗贼(Thief)。每个角色都有基本的属性,如生命值(health)、攻击力(attackPower),同时还有一些独特的技能。此外,游戏中还有一些通用的行为,如移动(move)和攻击(attack)。

我们可以通过类别为角色类添加一些通用的行为,通过委托将一些复杂的逻辑委托给其他类,同时利用协议多态性来实现不同角色的独特技能。

首先定义一个基础的 Character 类:

// Character.h
#import <Foundation/Foundation.h>

@interface Character : NSObject
@property (nonatomic, assign) NSInteger health;
@property (nonatomic, assign) NSInteger attackPower;
@end

// Character.m
#import "Character.h"

@implementation Character
@end

然后使用类别为 Character 类添加通用的移动方法:

// Character+Movement.h
#import "Character.h"

@interface Character (Movement)
- (void)move;
@end

// Character+Movement.m
#import "Character+Movement.h"

@implementation Character (Movement)
- (void)move {
    NSLog(@"Character is moving.");
}
@end

接着定义一个协议 CombatProtocol,用于描述攻击行为:

// CombatProtocol.h
#import <Foundation/Foundation.h>

@protocol CombatProtocol <NSObject>
- (void)attack;
@end

定义 Warrior 类,它遵循 CombatProtocol 协议,并实现独特的攻击方法:

// Warrior.h
#import "Character.h"
#import "CombatProtocol.h"

@interface Warrior : Character <CombatProtocol>
@end

// Warrior.m
#import "Warrior.h"

@implementation Warrior
- (void)attack {
    NSLog(@"Warrior attacks with sword!");
}
@end

定义 Mage 类,同样遵循 CombatProtocol 协议,但实现不同的攻击方法:

// Mage.h
#import "Character.h"
#import "CombatProtocol.h"

@interface Mage : Character <CombatProtocol>
@end

// Mage.m
#import "Mage.h"

@implementation Mage
- (void)attack {
    NSLog(@"Mage casts a spell!");
}
@end

定义 Thief 类,遵循 CombatProtocol 协议并实现自己的攻击方法:

// Thief.h
#import "Character.h"
#import "CombatProtocol.h"

@interface Thief : Character <CombatProtocol>
@end

// Thief.m
#import "Thief.h"

@implementation Thief
- (void)attack {
    NSLog(@"Thief attacks with dagger!");
}
@end

现在,我们可以在游戏逻辑中使用这些角色类:

#import <Foundation/Foundation.h>
#import "Character.h"
#import "Warrior.h"
#import "Mage.h"
#import "Thief.h"

void performCombat(id<CombatProtocol> character) {
    [character attack];
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Warrior *warrior = [[Warrior alloc] init];
        [warrior move];
        performCombat(warrior);

        Mage *mage = [[Mage alloc] init];
        [mage move];
        performCombat(mage);

        Thief *thief = [[Thief alloc] init];
        [thief move];
        performCombat(thief);
    }
    return 0;
}

在上述代码中,通过类别为 Character 类添加了通用的移动方法,模拟了多继承中行为的复用。同时,通过协议 CombatProtocol,不同的角色类(WarriorMageThief)以多态的方式实现了攻击行为。这种综合应用使得游戏角色的设计既具备了通用的功能,又有各自独特的行为,构建出灵活且易于扩展的游戏架构。

通过类别、委托、组合等方式模拟多继承,以及利用协议多态性,Objective-C 开发者可以构建出强大、灵活且易于维护的软件系统,充分发挥这门语言的特性和优势,满足各种复杂的业务需求。无论是在小型应用还是大型项目中,这些技术都有着广泛的应用场景和重要的作用。