Swift学习笔记20——协议(Protocols)
2015-10-07 13:51
459 查看
协议,就是一些规定。当一个类遵循了协议之后,它就必须满足协议所规定的内容。
使用协议的好处就是,以后我们可以面向协议编程。我们写的同一段代码可以针对不同的类型使用,只要这个类型遵循了我们所定义的协议。
协议定义语法
属性要求
协议里面可以定义一些实例属性或类型属性,那么遵循这个协议的类型就必须提供这些属性。可以是用存储属性或计算属性提供。
假如协议里面定义的属性是可以读写的。那么类型必须提供可读写的属性。如果协议定义只读的,那么类型里面可以是读写的。
方法要求
例子如上,协议里面的方法不用写方法体。如果是类型方法,加上关键字static。类在实现类型方法的时候,可以用class或static修饰。
注意最后一个实例方法,它加上了mutating关键字。这个主要是给值类型使用的。因为如果值类型在方法里面修改属性的话,必须用到mutating去修饰方法。如果是类实现这个协议的话,那么这个方法不用加mutating关键字。
构造器要求
如果一个类实现协议的构造器刚好和父类的构造器是一样的,那么要同时加上required和override关键字。
Swift中的协议可以当做类型来使用
把协议当做类型使用,打个比方,就是把协议当做Int使用。Int能使用的地方,协议都可以使用。比如定义一个协议类型的变量。用协议类型的变量做函数的参数,返回值。
这里的用法和java是一样的。和OC不同。OC是用id<SomeProtocol>来代表一个实现协议的类型。
利用扩展来实现协议
假如我们已经有了一个Animal类,但是不能获取到它的源代码。可以我们又想要它去遵守一定的协议,这时候可以用扩展为其添加协议。但是因为是扩展,所以还是不给添加存储属性。这时候必须用计算属性来实现协议。下面我们让Animal类实现上面的FruitProtocol协议。
继承协议
协议和类一样,是可以继承的。继承后的协议可以再添加一些属性和方法。如果需要继承多个协议,那么就用逗号分开多个协议。
Class协议
如果你的协议只想用在类里面,那么可以声明它为一个Class协议。这种协议不能被值类型实现。语法就是在继承协议的冒号后面加上class关键字。没继承协议的话,后面的协议就不用写了。但是class关键字一定要写在被继承协议的前面。
遵守多个协议
上面提到了,协议可以看成是一种类型,然后可以充当参数,返回值等等。但是有些时候想要一个参数同时满足多个协议。这个时候就不能用单个协议去充当参数了。这时候可以用一对<>号将多个协议连接起来。例子如下
判断实例是否遵守协议
is,这个is用于判断某个实例是否遵守了某个协议。
as?和as!。这个和类转换一样,用于将一个实例转换为某个协议类。如果这个实例遵守了这个协议,那么它就能被转换。
可选需求
上面所讲的所有定义在协议里面的东西都是必须实现的。但是有些情况下,我们想这些需求不一定要实现,而是可选地实现。这个时候就要用到可选需求。这个概念应该是直接利用了OC里面的概念来使用的。语法有两处:一处是@objc,一处是optional。
@objc一般是用于Swift和OC的混合编程。但是这里即使你没有使用到混合编程,如果想要实现可选需求,还是必须加上@objc。
使用到可选需求的时候要注意一点奇怪的事情。
第一,实现带有@objc的协议的时候,实现的内容上必须加上@objc。否则报错。这个@objc可以加在实现的属性和方法上,也可以加在类的声明关键字的最前面,但是后一种方法要求这个类必须是NSObject的子类。
第二,如果是在类扩展里面实现带有@objc的协议,只能在实现的内容上面加上@objc。
第三条,估计是个bug,当你定义一个继承自可选协议A的协议B。然后让类C实现这个协议B,用is判断C的实例是否是A的类型,照理来讲应该是true。可是这里会出现runtime Error。
扩展协议
这个和扩展类是一样的。但是这里并不是协议,里面的属性和方法都必须按照扩展的要求来写:方法要有方法体,不能有存储属性。协议扩展里的东西对实现了该协议的类都会有扩展作用。
Swift仍需改进。
使用协议的好处就是,以后我们可以面向协议编程。我们写的同一段代码可以针对不同的类型使用,只要这个类型遵循了我们所定义的协议。
协议定义语法
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仍需改进。
相关文章推荐
- 【Swift】学习笔记(一)——熟知 基础数据类型,编码风格,元组,主张
- Swift与OC代码转换实例
- swift中的as?和as!
- Swift - 表格图片加载优化(拖动表格时不加载,停止时只加载当前页图片)
- Swift - 给图片添加滤镜效果(棕褐色老照片滤镜,黑白滤镜)
- Swift - .plist文件数据的读取和存储
- Swift - 使用HTML5进行iOS开发(将HTML5打包成iOS应用)
- Swift - 禁用UIWebView和WKWebView的下拉拖动效果
- Swift - 异步加载各网站的favicon图标,并在单元格中显示
- Swift - 使用闭包筛选过滤数据元素
- Swift - 使用UI Dynamics给UIKit组件添加移动吸附行为
- Swift - 使用UI Dynamics给UIKit组件添加重力和碰撞行为
- Swift - 重写UIKit框架类的init初始化方法(以UITabBarController为例)
- Swift - 如何实现字符串的HMAC_SHA1加密
- Swift - 获取字符串的MD5值
- Swift - iOS中各种视图控制器(View Controller)的介绍
- Swift - 使用NSNotificationCenter发送通知,接收通知
- Swift - 使用NSURLSession同步获取数据(通过添加信号量)
- swift控件代码
- (IOS)Swift2.0 Radio 程序分析