特点
- 不同的类实现相似的功能
- 不同的类之间互相不干扰
声明
可以声明类方法、实例方法以及属性。
例如:
@protocol XYZPieChartViewDataSource
- (NSUInteger)numberOfSegments;
- (CGFloat)sizeOfSegmentAtIndex:(NSUInteger)segmentIndex;
- (NSString *)titleForSegmentAtIndex:(NSUInteger)segmentIndex;
@end
协议可以被任意对象实现,因此如果要声明一个协议对象,类型应当设置为id
,
@interface XYZPieChartView : UIView
@property (weak) id <XYZPieChartViewDataSource> dataSource;
...
@end
Objective-C 使用尖括号表示某个类实现了某个协议;同时作为一个类的属性时,协议对象应当被标记为 weak
,以防止循环引用导致的内存泄露。
默认情况下,在协议中声明的方法是 必须实现
的,这意味着任何声明了协议的对象都必须实现协议中声明的方法或属性。
通过 @ required
指令,在它之后声明的方法或属性为 必须实现
;
协议支持 可选
的方法或属性
通过 @optional
指令,在它之后声明的方法或属性为 可选
;
如下表所示:
@protocol XYZPieChartViewDataSource
- (NSUInteger)numberOfSegments;
- (CGFloat)sizeOfSegmentAtIndex:(NSUInteger)segmentIndex;
@optional
- (NSString *)titleForSegmentAtIndex:(NSUInteger)segmentIndex;
- (BOOL)shouldExplodeSegmentAtIndex:(NSUInteger)segmentIndex;
@required
- (UIColor *)colorForSegmentAtIndex:(NSUInteger)segmentIndex;
@end
在代码运行时需要判断 可选
的方法或函数是否被实现
NSString *thisSegmentTitle;
if ([self.dataSource respondsToSelector:@selector(titleForSegmentAtIndex:)]) {
thisSegmentTitle = [self.dataSource titleForSegmentAtIndex:index];
}
继承自其他协议的协议对象
- 协议可以继承另外一个协议
- 当声明一个协议时,它默认会继承 NSObject 协议
- 实现某个协议的对象,需要同时实现这个协议和它的父协议
@protocol MyProtocol <NSObject>
...
@end
MyProtocol 协议继承自 NSObject 协议
实现它的类不但需要实现 MyProtocol 协议,也要实现 NSObject 协议
遵照协议
一个类要遵照某个(某些)协议,写法如下:
@interface MyClass : NSObject <MyProtocol, AnotherProtocol, YetAnotherProtocol>
...
@end
一般情况下,某个类需要实现的协议不会太多
如果一个类中实现了过多的协议,则需要考虑重构它,把这个类拆分为多个类,分别实现不同的协议
同时一个协议也不宜实现过多的功能,实际应用中,需要将一个大的协议尽量拆解成多个协议来实现多种功能
协议是匿名使用的
可以不需要知道某个实例是哪种类型,而调用协议方法
id <XYZFrameworkUtility> utility =
[frameworkObject anonymousUtility];
NSUInteger count = [utility numberOfSegments];
另一个 CoreData 的例子:
NSInteger sectionNumber = ...
id <NSFetchedResultsSectionInfo> sectionInfo =
[self.fetchedResultsController.sections objectAtIndex:sectionNumber];
NSInteger numberOfRowsInSection = [sectionInfo numberOfObjects];