盒子
导航
文章目录
  1. 一、基本语法
  2. 二、相关解释
  3. 参考资料

Objective-C基础:Protocol

Learn Objective-C with 《Programming with Objective-C》。协议就是规则,在面向对象编程当中用来规范某个对象在某些情形下需要遵循的“行为规范”。例如一个Table View期望与Data Source(它可以是任何一个类对象)进行通信,以便能够知道它需要将什么数据显示出来(至于怎么显示那是Table View自己的事情),既然有通信,就意味着需要有规则和协议(都懂得汉语的人之间对话就是一种通信,而语言本身的语义等就是规则),Table View需要向Data Source发送一系列消息,而Data Source则需要对这些消息作出响应。Protocol就是定义了这些“通信消息及其响应”。

为了能够让Table View知道一个类对象是否是合格的DataSource,这个类对象需要实现一系列消息,以满足二者之间的通信规则(例如你想自己编写软件帮你实现一些东西,那么你就要懂得编程语言,而这个“编程语言”就是protocol)。Objective-C允许我们通过Protocol来定义这些规则,以及如何规定让一个类对象遵从这些“规则”。

一、基本语法

类通过定义属性和方法来规定了类对象所拥有的值及其行为,这些属性和方法是依赖于一个具体类的(在类当中定义的嘛),而Protocol则是定义了独立的,不依赖于具体某个类的,但可以被某些类遵从的属性和方法的集合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@protocol ProtocolName
// list of methods and properties
@end

//使用协议继承,可以像类一样,从父协议继承
@protocol MyProtocol <NSObject>
//...
@end

//让类遵从协议
@interface MyClass : NSObject <MyProtocol>
//...
@end

//遵从多个协议
@interface MyClass : NSObject <MyProtocol, AnotherProtocol, YetAnotherProtocol>
//...
@end

二、相关解释

Protocol可以定义类方法和实例方法,也可以定义属性。这么看来其实跟类还是差不多的。使用尖括号的方式来表明一个类、对象、属性遵从某个协议,例如:

@protocol XYZPieChartViewDataSource

  • (NSUInteger)numberOfSegments;
  • (CGFloat)sizeOfSegmentAtIndex:(NSUInteger)segmentIndex;
  • (NSString *)titleForSegmentAtIndex:(NSUInteger)segmentIndex;
    @end

在下面这个类当中声明了一个属性,这个属性遵从了上面定义的协议,由于协议是具体类无关的,所以下面这个属性的类型使用了可代表各种类对象的id类型。注意: 在对象中应用于委托和数据源的属性,为了避免循环强引用,所以往往被声明为weak弱引用,下面的dataSource就被声明为弱引用。

@interface XYZPieChartView : UIView
@property (weak) id dataSource;
//…
@end

对于上面的这个属性,如果尝试赋值给这个属性一个不遵从其指定的协议的对象(这里是XYZPieChartViewDataSource),那么编译器将会发出警告。

默认情况下协议当中定义的方法都是必须的,即一个遵从该协议的类对象必须实现这些方法。可以通过在协议中使用@optional关键字来声明一个方法是遵从该协议的类对象可选实现的。@optional@required也是一样)关键字将其之后直到@end末尾,或者其他指示命令(如@required)之前的方法都标记为可选的,即你不需要在每个方法前都写上@optional。如果协议当中一个方法是可选实现的,那么在写代码的时候必须测试这个方法是否意见实现,然后再使用,例如:

NSString *thisSegmentTitle;
//测试titleForSegmentAtIndex:方法是否被实现
if ([self.dataSource respondsToSelector:@selector(titleForSegmentAtIndex:)]) {
thisSegmentTitle = [self.dataSource titleForSegmentAtIndex:index];
}

If you attempt to call the respondsToSelector: method on an id conforming to the protocol as it’s defined above, you’ll get a compiler error that there’s no known instance method for it. Once you qualify an id with a protocol, all static type-checking comes back; you’ll get an error if you try to call any method that isn’t defined in the specified protocol. One way to avoid the compiler error is to set the custom protocol to adopt the NSObject protocol.

粗浅翻译:

如果尝试对一个遵循该协议的id类型的对象调用respondsToSelector:方法,将会出现编译错误,因为这个匿名的id类型对象没有任何实例方法;而如果指明了id类型对象(即转换成了具体某个类对象),那么所有的静态类型检查都会起作用,通过该对象调用不在所遵循的协议中的方法将会出现编译错误。避免这些编译错误的方法之一是让这个协议遵从NSObject协议(使用继承)。

注意,编译器不会为所遵从的协议中的属性自动合成实例和访问器方法。遵从多个协议时,必须实现所有协议中规定的必须实现的方法,如果没有实现,编译器会报警告。可以使用协议来实现隐藏类及其对象,类对象的行为通过协议来对外公布,而不允许外部使用类的实例。

参考资料

Programming with Objective-C》- Working with Protocols