Objective-C学习之Protocols

By | 2014/04/18

使用协议

在现实生活中人们在正式的商业活动中往往遵循一些严格的规程来处理一些特定的问题,在相关的合同中经常会出现遵循协议的字眼,但现实生活中的协议和OC中的协议有何区别呢?好那么我们就来讨论一下OC中的协议看看他们有何不同。

在面向对象的世界中,给对象制定一系列行为规定是十分必要的。例如一个tableview想要去访问一个数据源对象以展示其需要展示的数据。这就意味着数据源必须对tableview的请求作出相关的响应。这个数据源可以是任何类的实例,例如viewController或者是一个专门的数据源类。为了能够让tableview知道该对象适不适合做数据源,那么就必须在该对象中声明和定义一些列必要的方法。OC允许你去定义protocols来声明在特定请况下使用的方法。本博客将详细解释如何去使用协议来完成相应的工作。

 协议定义了协议契约

类中的interface声明了与其联系密切的属性和方法。协议(protocol)则正相反,他声明的属性和消息通常与该类的联系不是那么的紧密。下面来看一下protocol的基本语法:

[code lang=”objc”]
@protocol ProtocolName
// list of methods and properties
@end
[/code]

protocol可以声明类方法,成员方法以及属性。

下面我们来举一个例子,假设一个用一个用户自定义的视图来展示一个饼图,为了能够尽可能的重用视图,那些对显示什么内容的代码应该放在单独一个类中,我们称该类为datasource。这就意味着该类的多个实例可以根据不同的datasource来展示不同的信息。

这个饼图需要的基本信息是这个饼图被分成了多少块,每一块的大小以及每一块的标题,这个饼图的datasource protocol应该看起来向下面展示的那样:

[code lang=”objc”]
@protocol XYZPieChartViewDataSource
– (NSUInteger)numberOfSegments;
– (CGFloat)sizeOfSegmentAtIndex:(NSUInteger)segmentIndex;
– (NSString *)titleForSegmentAtIndex:(NSUInteger)segmentIndex;
@end
[/code]

这个饼图的类需要一个变量来记录这个datasource对象。这个对象可以是任何类,所以属性类型可以设为id,但这个id必须遵循这个相关的protocol。

其语法如下所示:

[code lang=”objc”]
@interface XYZPieChartView : UIView
@property (weak) id <XYZPieChartViewDataSource> dataSource;

@end
[/code]

注意这里使用了weak来比避免强引用循环,例如:一个viewController(datasource)中有一个smileFace对象的属性,在simileFace对象中有一个datasource,该datasource指向viewController,所以如果此处是strong的话就会形成强引用循环。

protocols存在可选方法

默认的情况下,在protocol中声明的方法都必须实现,这就意味着所以遵循该协议的类必须实现该协议中的方法。但是在protocol中也可以声明一些可以不被实现的方法。

就像上例(饼图)中的title如果可以不必出现在饼图中,那么data source对象就可以不必实现titleForSegmentAtIndex:方法。其语法如下所示:

[code lang=”objc”]
@protocol XYZPieChartViewDataSource
– (NSUInteger)numberOfSegments;
– (CGFloat)sizeOfSegmentAtIndex:(NSUInteger)segmentIndex;
@optional
– (NSString *)titleForSegmentAtIndex:(NSUInteger)segmentIndex;
@end
[/code]

在这种情况下只有titleForSegmentAtIndex:方法是被标记为可选的,前面的方法默认都是必选的。

还可以以下面的方法来标记:

[code lang=”objc”]
@protocol XYZPieChartViewDataSource
– (NSUInteger)numberOfSegments;
– (CGFloat)sizeOfSegmentAtIndex:(NSUInteger)segmentIndex;
@optional
– (NSString *)titleForSegmentAtIndex:(NSUInteger)segmentIndex;
– (BOOL)shouldExplodeSegmentAtIndex:(NSUInteger)segmentIndex;
@required
– (UIColor *)colorForSegmentAtIndex:(NSUInteger)segmentIndex;
@end
[/code]

在运行的时候检测可选方法是否实现

如果一个方法被标记为可选,那么在调用他的时候必须要去检查它是否实现了。方法如下:

[code lang=”objc”]
NSString *thisSegmentTitle;
if ([self.dataSource respondsToSelector:@selector(titleForSegmentAtIndex:)]) {
thisSegmentTitle = [self.dataSource titleForSegmentAtIndex:index];
}
[/code]

protocol继承自另一个protocol

语法如下:

[code lang=”objc”]
@protocol MyProtocol <NSObject>

@end
[/code]

遵循协议

语法如下:

[code lang=”objc”]
@interface MyClass : NSObject <MyProtocol>

@end

@interface MyClass : NSObject <MyProtocol, AnotherProtocol, YetAnotherProtocol>

@end
[/code]

protocol用于匿名类

当一个类不知是什么类型的时候protocol就派上用场了,例如一个框架的开发人可能选择不公开某个类的interface。因为类名不知道所以使用框架的用户无法直接创建该类的对象。那么可以使用protocol来完成相关对象的创建,代码如下:

[code lang=”objc”]
NSInteger sectionNumber = …
id <NSFetchedResultsSectionInfo> sectionInfo =
[self.fetchedResultsController.sections objectAtIndex:sectionNumber];
NSInteger numberOfRowsInSection = [sectionInfo numberOfObjects];
[/code]

发表评论

邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据