您的位置:首页 > 移动开发 > Swift

Swift学习笔记20——协议(Protocols)

2015-10-07 13:51 459 查看
协议,就是一些规定。当一个类遵循了协议之后,它就必须满足协议所规定的内容。

使用协议的好处就是,以后我们可以面向协议编程。我们写的同一段代码可以针对不同的类型使用,只要这个类型遵循了我们所定义的协议。

协议定义语法

protocol SomeProtocol {
// protocol definition goes here
}
类型实现协议的语法。类型可以是类、结构体、枚举类。协议写在父类的后面,用逗号分开。如果有多个协议,也用逗号分开。

class SomeClass: SomeSuperclass, FirstProtocol, AnotherProtocol {
// class definition goes here
}


属性要求
协议里面可以定义一些实例属性或类型属性,那么遵循这个协议的类型就必须提供这些属性。可以是用存储属性或计算属性提供。

假如协议里面定义的属性是可以读写的。那么类型必须提供可读写的属性。如果协议定义只读的,那么类型里面可以是读写的。

protocol FruitProtocol {
static var name: String {get}
var price: Int {set get}  //set 和 get 没顺序要求
}

class Apple: FruitProtocol {
static var name: String = "apple"
var price: Int {
get {
return 10
}
set {
print("I don't want to change the price!!")
}
}
}


方法要求

protocol SomeProtocol {
static func someTypeFunc()
func someFunc()
mutating func someMutatingFunc()
}


例子如上,协议里面的方法不用写方法体。如果是类型方法,加上关键字static。类在实现类型方法的时候,可以用class或static修饰。

注意最后一个实例方法,它加上了mutating关键字。这个主要是给值类型使用的。因为如果值类型在方法里面修改属性的话,必须用到mutating去修饰方法。如果是类实现这个协议的话,那么这个方法不用加mutating关键字。

构造器要求

protocol SomeProtocol {
init(someParameter: Double)
init?(someParameter: Int)
}
当一个类实现这种协议的时候,它如果不是一个final类(不允许继承),那么它在实现这些构造器的时候,构造器必须加上required关键字。

如果一个类实现协议的构造器刚好和父类的构造器是一样的,那么要同时加上required和override关键字。

Swift中的协议可以当做类型来使用

把协议当做类型使用,打个比方,就是把协议当做Int使用。Int能使用的地方,协议都可以使用。比如定义一个协议类型的变量。用协议类型的变量做函数的参数,返回值。

这里的用法和java是一样的。和OC不同。OC是用id<SomeProtocol>来代表一个实现协议的类型。

protocol SomeProtocol {
init(someParameter: Double)
init?(someParameter: Int)
}

var p: SomeProtocol? = nil
func someFunc(p: SomeProtocol) -> SomeProtocol {
return p
}


利用扩展来实现协议

假如我们已经有了一个Animal类,但是不能获取到它的源代码。可以我们又想要它去遵守一定的协议,这时候可以用扩展为其添加协议。但是因为是扩展,所以还是不给添加存储属性。这时候必须用计算属性来实现协议。下面我们让Animal类实现上面的FruitProtocol协议。

extension Animal: FruitProtocol {
var price: Int {
get{
return 23434
}
set {
print("do nothing")
}
}
static var name: String {
return "animal"
}
}
如果一个类在定义的时候已经满足某个协议的所有需求,那么可以使用extension显式指明它遵守了这个协议。这个时候,extension里面不用写什么。另外如果一个类满足了某个协议的需求,你不显式声明它遵守这个协议的话,它是不实现这个协议的。

继承协议

协议和类一样,是可以继承的。继承后的协议可以再添加一些属性和方法。如果需要继承多个协议,那么就用逗号分开多个协议。

protocol AnimalProtocol: FruitProtocol, SecondProtocol{
var canRun: Bool {get}
func makeSounds()
}


Class协议

如果你的协议只想用在类里面,那么可以声明它为一个Class协议。这种协议不能被值类型实现。语法就是在继承协议的冒号后面加上class关键字。没继承协议的话,后面的协议就不用写了。但是class关键字一定要写在被继承协议的前面。

protocol AnimalProtocol:class, FruitProtocol, SecondProtocol{
var canRun: Bool {get}
func makeSounds()
}


遵守多个协议

上面提到了,协议可以看成是一种类型,然后可以充当参数,返回值等等。但是有些时候想要一个参数同时满足多个协议。这个时候就不能用单个协议去充当参数了。这时候可以用一对<>号将多个协议连接起来。例子如下

func doSomething(para: protocol<FruitProtocol,SecondProtocol>) {
//do something
}
这时候,para参数就必须同时遵守这两个协议才行。

判断实例是否遵守协议

is,这个is用于判断某个实例是否遵守了某个协议。

as?和as!。这个和类转换一样,用于将一个实例转换为某个协议类。如果这个实例遵守了这个协议,那么它就能被转换。

可选需求

上面所讲的所有定义在协议里面的东西都是必须实现的。但是有些情况下,我们想这些需求不一定要实现,而是可选地实现。这个时候就要用到可选需求。这个概念应该是直接利用了OC里面的概念来使用的。语法有两处:一处是@objc,一处是optional。

@objc protocol SecondProtocol {
optional var time: Double {get}
optional func doHomework()
}
关键字optional用来定义这个需求是不是可选的。

@objc一般是用于Swift和OC的混合编程。但是这里即使你没有使用到混合编程,如果想要实现可选需求,还是必须加上@objc。

使用到可选需求的时候要注意一点奇怪的事情。

第一,实现带有@objc的协议的时候,实现的内容上必须加上@objc。否则报错。这个@objc可以加在实现的属性和方法上,也可以加在类的声明关键字的最前面,但是后一种方法要求这个类必须是NSObject的子类。

第二,如果是在类扩展里面实现带有@objc的协议,只能在实现的内容上面加上@objc。

第三条,估计是个bug,当你定义一个继承自可选协议A的协议B。然后让类C实现这个协议B,用is判断C的实例是否是A的类型,照理来讲应该是true。可是这里会出现runtime Error。

扩展协议

这个和扩展类是一样的。但是这里并不是协议,里面的属性和方法都必须按照扩展的要求来写:方法要有方法体,不能有存储属性。协议扩展里的东西对实现了该协议的类都会有扩展作用。

extension AnimalProtocol {
var canFly: Bool {
get{
return false
}
}
}
注意,如果你用协议扩展对协议的必须方法或属性做了默认的定义。那么类在实现这个协议的时候,不必实现已经实现的协议要求。但如果实现了,协议扩展里面实现的内容都会被类里面的定义覆盖。

Swift仍需改进。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: