协议定义了一个方法的蓝图,属性和其他适合特定任务或功能的要求。协议实际上并不提供一个这些要求的实现,它只是描述了一个实现会是什么样子。协议可以通过一个类,结构或枚举提供这些要求的具体实现。满足要求的任何类型的协议都是符合协议。
协议可以要求符合类型有特定的实例属性,实例方法,类型丰富,操作符和下标。
1. 协议的语法
协议名放在类型名之后,用冒号分割,当作定义的一部分。可以列出多个协议,由逗号分隔。
如果一个类有父类,在任何协议之前列出父类名,后跟一个逗号。
protocol FirstProtocol {
// protocol definition goes here
}
protocol AnotherProtocol {
// protocol definition goes here
}
class SomeSuperclass{
}
class SomeClass: SomeSuperclass,FirstProtocol,AnotherProtocol {
// protocol definition goes here
}
2. 属性要求
属性要求总是声明为变量属性,用var关键字做前缀。可获取和可设置属性是通过在他们类型声明后编写{ get set }方法,并且可获取属性是通过编写{ get }方法。
协议例子如下:
protocol ExampleProtocol {
var simpleDescription: String { get }
//在协议中定义方法时需要注意,类中的方法因为本身就可以改变类不需要mutating关键字修饰,但是结构struct和枚举enum使用协议时,协议和struct,enum都需要在定义和使用方法前用mutating关键字修饰
mutating func adjust()
}
2.1 结构struct使用协议
//结构体使用协议
struct SimpleStructure: ExampleProtocol {
var simpleDescription: String = "A simple "
mutating func adjust() {
simpleDescription += "struct."
}
}
var b = SimpleStructure()
b.adjust()
let bDescription = b.simpleDescription
print(bDescription) //"A simple struct.\n"
//结构体重的方法如果需要改变结构体本身 使用的方法需要使用mutating进行修饰,如果说我们在上面的adjust中不对结构体做任何修改的话,比如说单纯的打印一段文字,不加mutating也是可以的。
2.2 类class使用协议
//类使用协议
class SimpleClass: ExampleProtocol {
//如果在编译时不满足协议要求,Swift会报错
var simpleDescription: String = "A simple "
var anotherProperty: Int = 69105
func adjust() {
simpleDescription += "class."
}
}
var a = SimpleClass()
a.adjust()
let aDescription = a.simpleDescription
print(aDescription) //"A simple class.\n"
//类中的方法因为本身就可以改变类所以adjust方法不需要mutating关键字修饰
2.3 枚举enum使用协议
//枚举使用协议
enum SimpleEnum:ExampleProtocol {
case out
mutating func adjust() {
print(self.simpleDescription + "enum.")
}
var simpleDescription: String{
get {
switch self {
case .out:
return "A simple "
}
}
}
}
var c = SimpleEnum.out
c.adjust()
//输出:A simple enum.
3. 方法要求
协议可以要求指定实例方法和类型方法被一致的类型实现。这些方法被写为协议定义的一部分,跟普通实例和类型方法完全一样,但是没有大括号或方法体。可变参数是允许的,普通方法也遵循同样的规则。
3.1 方法协议
//协议内定义方法使用static进行定义,但是在实现时还是按照上面的规则:在struct或enum中仍然使用static。在class里使用class关键字时表示可override的类型方法实现,而在class里使用static关键字时表示不可override的类型方法实现
protocol MyProtocol {
static func foo() -> String
}
struct MyStruct: MyProtocol {
static func foo() -> String {
return "MyStruct"
}
}
enum MyEnum: MyProtocol {
static func foo() -> String {
return "MyEnum"
}
}
class MyClass: MyProtocol {
class func foo() -> String {
return "MyClass,可override的类型方法实现"
}
}
class MyClass1: MyProtocol {
static func foo() -> String {
return "MyClass,不可override的类型方法实现"
}
}
3.2 构造方法协议
实现时用 required init(编译器会自动提示添加)
//必须实现,实现时用 required init(编译器会自动提示添加)
protocol 带参数的构造方法协议 {
init(某参数: Int)
}
class 某类: 带参数的构造方法协议 { required init(某参数: Int) { } } protocol 构造方法协议 { init() } class 某类1: 构造方法协议 { required init() { } }
如果子类与父类同时遵从某构造方法协议,则子类构造方法须加override required
protocol 构造方法协议 {
init()
}
class 父类 {
init() {
}
}
class 子类: 父类,构造方法协议 {
override required init() {
}
}
4. 协议扩展
协议扩展: 即使无源码权限下,给已有的类添加协议.
//1.既存实例会自动遵从添加了的协议.
protocol ProtocolForInt {
var description:String {get}
mutating func addOne()
}
extension Int:ProtocolForInt {
mutating func addOne() {
self += 1
}
var description: String {
return "This number is \(self)"
}
}
var abc:Int = 4
abc.addOne() //5
print(abc.description) //"This number is 5\n"
//2.如果一个类型预遵从了协议,可以直接扩展协议
struct MyText {
var text: String
//注意:即使满足了协议要求,类型也不会自动转变,必须为它做出明显的协议声明。
var description: String {
return "我的自定义显示:" + text
}
}
//CustomStringConvertible协议内含有必须实现的var description: String参数的可获取方法
extension MyText: CustomStringConvertible {}//协议扩展
let text1 = MyText(text: "3天学会Swift 3")
print(text1)
/* 协议扩展时text1输出为"我的自定义显示:3天学会Swift 3\n" 屏蔽上面的协议扩展则text1输出为"MyText(text: "3天学会Swift 3")\n" */
5. 协议的继承
协议能够继承一到多个协议。语法与类继承基本相同。
protocol InheritingProtocol: SomeProtocol,AnotherProtocol { }
6. 协议实现
6.1 协议的默认实现
//自定义一个可打印和可预览协议
//CustomStringConvertible协议需满足var description: String要求
//CustomPlaygroundQuickLookable协议需满足var customPlaygroundQuickLook: PlaygroundQuickLook要求
protocol MyPrintable: CustomStringConvertible,CustomPlaygroundQuickLookable {
}
//提供默认实现: 可以给协议扩展提供一个默认的实现,任何遵从此协议的类型都会获得.
extension MyPrintable {
var description: String {
return "控制台: 默认描述"
}
var customPlaygroundQuickLook: PlaygroundQuickLook {
return PlaygroundQuickLook.text("playgroud预览: 默认值")
// return PlaygroundQuickLook.color(UIColor.blue)
}
}
//1.采用默认实现
struct MyText {
var text: String
}
//MyText采用默认实现
extension MyText: MyPrintable {
}
let text1 = MyText(text: "abcd") //"控制台: 默认描述\n"
print(text1) //"控制台: 默认描述\n"
//2.不采用默认实现
struct MyText1 {
var text: String
}
//MyText1不采用默认实现
extension MyText1: MyPrintable {
var description: String {
return "print时的预览:" + self.text
}
var customPlaygroundQuickLook: PlaygroundQuickLook {
return PlaygroundQuickLook.text("我的Text快速预览:" + self.text)
}
}
let text2 = MyText1(text: "efg") //"我的Text快速预览:efg"
print(text2) //"print时的预览:efg\n"
6.2 协议中的必需实现和可选实现方法
//协议扩展
//swift 中可选协议方法的实现
protocol SwiftDiagonProtocol{
//必须
func requiredMethod()
//可选
func optionalMethod()
}
//方法1:空实现(方法2:将可选实现和必需实现分为2个协议)
extension SwiftDiagonProtocol{
func optionalMethod(){
//空实现也可以
}
}
class SwiftClass: SwiftDiagonProtocol{
func requiredMethod() {
//Someting
}
//如果需要实现可选方法则实现,这里的实现会覆盖extension中的默认实现
func optionalMethod(){
//Something
}
}
7. 类专用协议
可以在协议的继承列表中,通过添加关键字『class』,限制协议只能适配到类(class)型,结构和枚举不能遵循该协议。
protocol 一个协议: class,CustomPlaygroundQuickLookable {
}
class myText {
var text = "22"
}
extension myText: 一个协议{
var customPlaygroundQuickLook: PlaygroundQuickLook {
return PlaygroundQuickLook.text(self.text)
}
}
let text = myText() //22
text.text //22
text.customPlaygroundQuickLook //text("22")
print(text) //输出"myText\n",text本身仍然是myText类
8. 协议组合
协议组合: 多个协议临时组合在一起的类型. 形式: 协议1 & 协议2 & …>
protocol 年龄协议 {
var 年龄 : Int { get }
}
protocol 姓名协议 {
var 姓名: String { get }
}
struct 学生: 年龄协议,姓名协议,CustomPlaygroundQuickLookable {
var 年龄: Int
var 姓名: String
var customPlaygroundQuickLook: PlaygroundQuickLook {
return PlaygroundQuickLook.text(self.姓名 + ":\(self.年龄)岁")
}
}
//无论传递的寿星是何类型,只要遵从年龄和姓名协议即可,参数大大自由化.
func 生日祝愿(寿星: 年龄协议 & 姓名协议) {
print("祝",寿星.姓名,寿星.年龄,"岁生日快乐!")
}
let 学生1 = 学生(年龄: 20,姓名: "洪荒少女") //"洪荒少女:20岁"
生日祝愿(寿星: 学生1) //输出:祝 洪荒少女 20 岁生日快乐!
9. 协议的检查和转换
9.1 协议的检查 is
struct Person: OneProtocol {
}
let p1 = Person()
if (p1 is OneProtocol){ //可以理解为:p1 是一个遵守了OneProtocol协议类型的实例
print("yes")
}
//输出:yes
9.2 协议的转换 as
protocol Coder {
var name :String {get set}
var updating: String { get }
}
struct AndroidCoder: Coder {
var name: String
var updating: String {
return "安卓程序员学iOS开发"
}
}
struct DotNotCoder: Coder {
var name: String
var updating: String {
return ".NET程序员学Swift 3"
}
}
struct NewBie {
var name: String
}
let a = AndroidCoder(name: "小米")
let d = DotNotCoder(name: "小猫")
let x = NewBie(name: "小狗")
let coders:[Any] = [a,d,x]
for coder in coders {
//as操作符用来把某个实例转型为另外的类型,由于实例转型可能失败,因此Swift为as操作符提供了两种形式:选项形式as?和强制形式as!
//as? 如果转换不成功的时候便会返回一个 nil 对象,如果能确保100%会成功的转换则可使用 as!,否则使用 as?
//as!由于是强制类型转换,如果转换失败会报 runtime 运行错误。
if let coder1 = coder as? Coder {
print(coder1.updating)
} else {
print("你不是程序员!你会很辛苦的!")
}
if let xiaobo = coder as? NewBie {
print("你是"+xiaobo.name)
}
}
/*输出: 安卓程序员学iOS开发 .NET程序员学Swift 3 你不是程序员!你会很辛苦的! 你是小狗 */
10. 类型约束
func findIndex<T: Equatable>(array: [T],valueToFind: T) -> Int? {
var index: Int?
for (i,value) in array.enumerated() {
if (value == valueToFind) {
index = i
}
}
return index
}
var testArray = [1,2,3,4]
findIndex(array: testArray,valueToFind: 3) //2
//swift标准库定义了一个Equatable协议,该协议要求任何遵循该协议的类型实现(==)和(!=)对任何两个该类型进行比较。所有的swift标准类型自动支持Equatable协议。
11. 其他
类中在协议中没有定义的属性,我们是无法访问的
protocol ProtocolForInt {
var description:String {get}
mutating func addOne()
}
class SimpleClassForInt:ProtocolForInt {
var number = 0
var description: String = "这是一个描述"
func addOne() {
number += 1
}
}
var simpleClassForInt:ProtocolForInt = SimpleClassForInt()
print(simpleClassForInt.description) //"这是一个描述\n"
simpleClassForInt.addOne()
simpleClassForInt //number:1,description:"这是一个描述"
//print(simpleClassForInt.number) //错误,类中在协议中没有定义的属性,无法访问
我们可以看到只有协议中定义的属性和方法是我们可以访问的,类中在协议中没有定义的属性,我们是无法访问的。
尽管simpleClassForInt的运行时(Runtime)类型是SimpleClassForInt,但是编译器还是会把它视为ProtocolForInt类型,所以在协议之外的属性和方法在simpleClassForInt中是无法访问的。
参考自
SwiftV课堂视频源码
http://c.biancheng.net/cpp/html/2433.html
http://www.wendq.com/wd/201702/14136.html
http://www.jianshu.com/p/09d312dac288
http://www.jb51.cc/article/p-azvphnod-qq.html