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

学会在Objective-C中使用协议进行对象交互

2022-10-076.2k 阅读

一、Objective-C 协议基础

1.1 协议的定义

在Objective-C中,协议(Protocol)是一种特殊的类型,它定义了一系列方法的声明,但并不提供这些方法的实现。协议就像是一个契约,规定了遵循该协议的对象必须实现某些特定的方法。定义协议使用@protocol关键字,语法如下:

@protocol ProtocolName <NSObject>
// 方法声明
- (void)method1;
- (NSString *)method2WithParameter:(NSString *)param;
@end

在上述代码中,@protocol声明了一个名为ProtocolName的协议。<NSObject>表示该协议继承自NSObject协议,NSObject协议定义了一些基本的方法,如descriptionhash等,几乎所有的Objective-C对象都遵循NSObject协议。协议中可以声明实例方法(如method1method2WithParameter:),也可以声明类方法(使用+号前缀)。

1.2 协议的分类

  1. 非正式协议:在早期的Objective-C中,非正式协议是通过类别(Category)来实现的。一个类可以通过类别来声明遵循某个非正式协议,并选择性地实现其中的方法。不过,这种方式现在已经不推荐使用,因为它存在一些局限性,比如可能会导致方法名冲突等问题。

  2. 正式协议:即我们现在使用@protocol定义的协议。正式协议要求遵循它的类必须明确声明,并尽可能实现协议中定义的方法(对于可选方法可以选择不实现)。正式协议提供了更严格的类型检查和契约约束,是目前在Objective-C中进行对象交互的主要方式。

二、类遵循协议

2.1 声明遵循协议

一个类要遵循某个协议,需要在类的声明中指出。例如:

@interface MyClass : NSObject <ProtocolName>
// 类的其他属性和方法声明
@end

在上述代码中,MyClass类声明遵循了ProtocolName协议。这意味着MyClass类承诺实现ProtocolName协议中定义的所有必需方法(如果协议中有可选方法,也可以选择实现)。

2.2 实现协议方法

当一个类声明遵循某个协议后,就需要实现协议中的方法。以ProtocolName协议为例,MyClass类的实现如下:

@implementation MyClass
- (void)method1 {
    NSLog(@"执行 method1 方法");
}

- (NSString *)method2WithParameter:(NSString *)param {
    return [NSString stringWithFormat:@"接收到的参数是: %@", param];
}
@end

MyClass类的实现中,实现了ProtocolName协议中定义的method1method2WithParameter:方法。如果一个类没有实现协议中的必需方法,编译器会发出警告。

三、协议中的可选方法

3.1 声明可选方法

在协议定义中,可以使用@optional关键字来声明可选方法。例如:

@protocol ProtocolWithOptionalMethods <NSObject>
- (void)requiredMethod;
@optional
- (void)optionalMethod;
@end

在上述协议中,requiredMethod是必需方法,遵循该协议的类必须实现;而optionalMethod是可选方法,遵循协议的类可以选择实现。

3.2 检查对象是否实现可选方法

当调用一个遵循包含可选方法协议的对象的可选方法时,需要先检查该对象是否实现了该方法。可以使用respondsToSelector:方法来进行检查。示例如下:

@interface AnotherClass : NSObject <ProtocolWithOptionalMethods>
@end

@implementation AnotherClass
- (void)requiredMethod {
    NSLog(@"执行 requiredMethod 方法");
}
@end

// 使用示例
AnotherClass *obj = [[AnotherClass alloc] init];
if ([obj respondsToSelector:@selector(optionalMethod)]) {
    [obj optionalMethod];
} else {
    NSLog(@"对象未实现 optionalMethod 方法");
}

在上述代码中,AnotherClass类只实现了requiredMethod方法,未实现optionalMethod方法。通过respondsToSelector:方法检查后,发现对象未实现optionalMethod方法,从而打印相应的提示信息。

四、协议作为类型

4.1 协议类型的变量和参数

在Objective-C中,可以使用协议类型来声明变量、方法参数和返回值。例如:

@protocol Printable <NSObject>
- (void)print;
@end

@interface Printer : NSObject
- (void)printObject:(id<Printable>)obj;
@end

@implementation Printer
- (void)printObject:(id<Printable>)obj {
    [obj print];
}
@end

@interface Document : NSObject <Printable>
@end

@implementation Document
- (void)print {
    NSLog(@"打印文档内容");
}
@end

// 使用示例
Printer *printer = [[Printer alloc] init];
Document *document = [[Document alloc] init];
[printer printObject:document];

在上述代码中,Printable协议定义了一个print方法。Printer类的printObject:方法接受一个遵循Printable协议的对象作为参数。Document类遵循Printable协议并实现了print方法。最后,创建PrinterDocument对象,并调用printerprintObject:方法传入document对象,从而执行documentprint方法。

4.2 协议类型的数组和集合

不仅可以使用协议类型声明单个变量和参数,还可以用于数组、集合等容器。例如:

NSMutableArray<id<Printable>> *printableArray = [NSMutableArray array];
Document *doc1 = [[Document alloc] init];
Document *doc2 = [[Document alloc] init];
[printableArray addObject:doc1];
[printableArray addObject:doc2];

for (id<Printable> printable in printableArray) {
    [printable print];
}

在上述代码中,创建了一个NSMutableArray,其元素类型为遵循Printable协议的对象。向数组中添加两个Document对象,然后通过遍历数组,调用每个对象的print方法。

五、协议的继承

5.1 继承其他协议

一个协议可以继承自其他协议,从而获得父协议中定义的方法声明。例如:

@protocol BasicProtocol <NSObject>
- (void)basicMethod;
@end

@protocol AdvancedProtocol <BasicProtocol>
- (void)advancedMethod;
@end

在上述代码中,AdvancedProtocol协议继承自BasicProtocol协议。这意味着遵循AdvancedProtocol协议的类不仅需要实现advancedMethod方法,还需要实现BasicProtocol协议中定义的basicMethod方法。

5.2 多协议继承

一个协议也可以同时继承多个协议。例如:

@protocol ProtocolA <NSObject>
- (void)methodA;
@end

@protocol ProtocolB <NSObject>
- (void)methodB;
@end

@protocol CombinedProtocol <ProtocolA, ProtocolB>
- (void)methodC;
@end

在上述代码中,CombinedProtocol协议同时继承了ProtocolAProtocolB协议。遵循CombinedProtocol协议的类需要实现methodAmethodBmethodC方法。

六、协议与代理模式

6.1 代理模式简介

代理模式是一种常用的设计模式,在Objective-C中,协议常常与代理模式结合使用。代理模式的核心思想是:一个对象(代理对象)代表另一个对象(委托对象)来处理某些任务。委托对象将部分职责委托给代理对象,通过协议来定义委托对象和代理对象之间的交互接口。

6.2 代理协议的定义

以一个简单的视图控制器(ViewController)和它的代理为例,定义代理协议如下:

@protocol ViewControllerDelegate <NSObject>
@optional
- (void)viewControllerDidLoad:(UIViewController *)viewController;
- (void)viewControllerWillAppear:(UIViewController *)viewController;
@end

在上述协议中,定义了两个可选方法,用于通知代理视图控制器的加载和即将显示事件。

6.3 设置代理并实现代理方法

在视图控制器类中,需要定义一个代理属性,并在适当的时候调用代理方法。例如:

@interface MyViewController : UIViewController
@property (nonatomic, weak) id<ViewControllerDelegate> delegate;
@end

@implementation MyViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    if ([self.delegate respondsToSelector:@selector(viewControllerDidLoad:)]) {
        [self.delegate viewControllerDidLoad:self];
    }
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    if ([self.delegate respondsToSelector:@selector(viewControllerWillAppear:)]) {
        [self.delegate viewControllerWillAppear:self];
    }
}
@end

然后,在另一个类中,设置视图控制器的代理并实现代理方法:

@interface MyDelegateClass : NSObject <ViewControllerDelegate>
@end

@implementation MyDelegateClass
- (void)viewControllerDidLoad:(UIViewController *)viewController {
    NSLog(@"视图控制器 %@ 已加载", viewController);
}

- (void)viewControllerWillAppear:(UIViewController *)viewController {
    NSLog(@"视图控制器 %@ 即将显示", viewController);
}
@end

// 使用示例
MyViewController *viewController = [[MyViewController alloc] init];
MyDelegateClass *delegate = [[MyDelegateClass alloc] init];
viewController.delegate = delegate;

在上述代码中,MyViewController类定义了一个代理属性delegate,并在viewDidLoadviewWillAppear方法中调用代理方法。MyDelegateClass类实现了ViewControllerDelegate协议中的方法。最后,将MyDelegateClass的实例设置为MyViewController的代理,从而实现了视图控制器与代理之间的交互。

七、协议与通知中心

7.1 通知中心简介

通知中心(NSNotificationCenter)是Objective-C中实现观察者模式的一种机制。它允许对象发布通知(Notification),其他感兴趣的对象可以注册监听这些通知,并在通知发布时执行相应的操作。

7.2 结合协议使用通知中心

可以通过协议来定义通知的相关信息,使得代码结构更加清晰。例如,定义一个协议用于通知某个操作的完成:

@protocol OperationCompletionProtocol <NSObject>
@optional
- (void)operationDidComplete:(NSNotification *)notification;
@end

// 发布通知的类
@interface OperationClass : NSObject
- (void)performOperation;
@end

@implementation OperationClass
- (void)performOperation {
    // 模拟操作完成
    NSNotification *notification = [NSNotification notificationWithName:@"OperationCompleted" object:self];
    [[NSNotificationCenter defaultCenter] postNotification:notification];
}
@end

// 监听通知的类
@interface ListenerClass : NSObject <OperationCompletionProtocol>
@end

@implementation ListenerClass
- (void)operationDidComplete:(NSNotification *)notification {
    NSLog(@"操作已完成,通知对象: %@", notification.object);
}

- (void)registerForNotification {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(operationDidComplete:) name:@"OperationCompleted" object:nil];
}

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}
@end

// 使用示例
OperationClass *operation = [[OperationClass alloc] init];
ListenerClass *listener = [[ListenerClass alloc] init];
[listener registerForNotification];
[operation performOperation];

在上述代码中,OperationCompletionProtocol协议定义了操作完成时的处理方法。OperationClass类在操作完成时发布通知。ListenerClass类遵循协议并实现了操作完成的处理方法,同时注册监听通知。最后,创建OperationClassListenerClass的实例,启动操作并观察通知的处理。

八、协议的高级应用

8.1 协议扩展

在一些情况下,可能需要为遵循某个协议的对象添加一些额外的功能,但又不想在协议定义中直接添加方法(避免影响所有遵循该协议的类)。这时可以使用类别(Category)为协议添加扩展方法。例如:

@protocol MyProtocol <NSObject>
- (void)basicMethod;
@end

@interface MyClass : NSObject <MyProtocol>
@end

@implementation MyClass
- (void)basicMethod {
    NSLog(@"执行 basicMethod");
}
@end

// 协议扩展
@interface NSObject (MyProtocolExtensions) <MyProtocol>
- (void)extendedMethod;
@end

@implementation NSObject (MyProtocolExtensions)
- (void)extendedMethod {
    NSLog(@"执行扩展方法 extendedMethod");
}
@end

// 使用示例
MyClass *obj = [[MyClass alloc] init];
[obj basicMethod];
[obj extendedMethod];

在上述代码中,通过类别NSObject (MyProtocolExtensions)为遵循MyProtocol协议的对象添加了extendedMethod方法。这样,MyClass类虽然没有直接实现extendedMethod,但由于它遵循MyProtocol协议,也可以调用该扩展方法。

8.2 协议与泛型

Objective-C从iOS 6.0开始引入了泛型语法,这使得协议在集合类中的使用更加安全和清晰。例如,定义一个遵循协议的泛型数组:

@protocol Comparable <NSObject>
- (NSComparisonResult)compare:(id<Comparable>)other;
@end

@interface Person : NSObject <Comparable>
@property (nonatomic, strong) NSString *name;
- (instancetype)initWithName:(NSString *)name;
- (NSComparisonResult)compare:(id<Comparable>)other;
@end

@implementation Person
- (instancetype)initWithName:(NSString *)name {
    self = [super init];
    if (self) {
        _name = name;
    }
    return self;
}

- (NSComparisonResult)compare:(id<Comparable>)other {
    Person *otherPerson = (Person *)other;
    return [self.name compare:otherPerson.name];
}
@end

// 使用泛型数组
NSMutableArray<id<Comparable>> *comparableArray = [NSMutableArray array];
Person *person1 = [[Person alloc] initWithName:@"Alice"];
Person *person2 = [[Person alloc] initWithName:@"Bob"];
[comparableArray addObject:person1];
[comparableArray addObject:person2];

[comparableArray sortUsingComparator:^NSComparisonResult(id<Comparable>  _Nonnull obj1, id<Comparable>  _Nonnull obj2) {
    return [obj1 compare:obj2];
}];

for (Person *person in comparableArray) {
    NSLog(@"排序后的人员: %@", person.name);
}

在上述代码中,Comparable协议定义了一个compare方法用于比较对象。Person类遵循Comparable协议并实现了compare方法。通过定义NSMutableArray<id<Comparable>>泛型数组,使得数组中的元素必须遵循Comparable协议,从而在进行排序等操作时更加安全和规范。

通过以上内容,我们全面深入地探讨了在Objective-C中使用协议进行对象交互的各个方面,从基础定义到高级应用,希望能帮助开发者更好地掌握这一重要特性,编写出更加健壮和灵活的代码。