学会在Objective-C中使用协议进行对象交互
一、Objective-C 协议基础
1.1 协议的定义
在Objective-C中,协议(Protocol)是一种特殊的类型,它定义了一系列方法的声明,但并不提供这些方法的实现。协议就像是一个契约,规定了遵循该协议的对象必须实现某些特定的方法。定义协议使用@protocol
关键字,语法如下:
@protocol ProtocolName <NSObject>
// 方法声明
- (void)method1;
- (NSString *)method2WithParameter:(NSString *)param;
@end
在上述代码中,@protocol
声明了一个名为ProtocolName
的协议。<NSObject>
表示该协议继承自NSObject
协议,NSObject
协议定义了一些基本的方法,如description
、hash
等,几乎所有的Objective-C对象都遵循NSObject
协议。协议中可以声明实例方法(如method1
和method2WithParameter:
),也可以声明类方法(使用+
号前缀)。
1.2 协议的分类
-
非正式协议:在早期的Objective-C中,非正式协议是通过类别(Category)来实现的。一个类可以通过类别来声明遵循某个非正式协议,并选择性地实现其中的方法。不过,这种方式现在已经不推荐使用,因为它存在一些局限性,比如可能会导致方法名冲突等问题。
-
正式协议:即我们现在使用
@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
协议中定义的method1
和method2WithParameter:
方法。如果一个类没有实现协议中的必需方法,编译器会发出警告。
三、协议中的可选方法
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
方法。最后,创建Printer
和Document
对象,并调用printer
的printObject:
方法传入document
对象,从而执行document
的print
方法。
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
协议同时继承了ProtocolA
和ProtocolB
协议。遵循CombinedProtocol
协议的类需要实现methodA
、methodB
和methodC
方法。
六、协议与代理模式
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
,并在viewDidLoad
和viewWillAppear
方法中调用代理方法。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
类遵循协议并实现了操作完成的处理方法,同时注册监听通知。最后,创建OperationClass
和ListenerClass
的实例,启动操作并观察通知的处理。
八、协议的高级应用
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中使用协议进行对象交互的各个方面,从基础定义到高级应用,希望能帮助开发者更好地掌握这一重要特性,编写出更加健壮和灵活的代码。