Swift 协议

Posted 博BOBO

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Swift 协议相关的知识,希望对你有一定的参考价值。

协议的语法

  • 自定义类型声明时,将协议名放在类型名的冒号之后来表示该类型采纳一个特定的协议。多个协议可以用逗号分开列出。
  • 若一个类拥有父类,将这个父类名放在其采纳的协议名之前,并用逗号分隔。
protocol SomeProtocol 
    //定义了一个协议

protocol AnotherProtocol 
    //定义了一个协议

protocol OneProtocol: SomeProtocol, AnotherProtocol 
    //定义了一个协议 OneProtocol, 他分别遵循 SomeProtocol 和 AnotherProtocol 协议

class SomeSuperClass 
    //定义了一个基类

class SomeClas: SomeSuperClass, SomeProtocol, AnotherProtocol 
    //定义了一个类 SomeClas, 他有一个父类 SomeSuperClass, 并且他遵循了 SomeProtocol 和 AnotherProtocol 协议


属性要求

  • 协议可以要求所有遵循该协议的类型提供特定名字和类型的实例属性或类型属性。协议并不会具体说明属性是储存型属性还是计算型属性——它只具体要求属性有特定的名称和类型。协议同时要求一个属性必须明确是可读的或可读的和可写的。
  • 若协议要求一个属性为可读和可写的,那么该属性要求不能用常量存储属性或只读计算属性来满足。若协议只要求属性为可读的,那么任何种类的属性都能满足这个要求,而且如果你的代码需要的话,该属性也可以是可写的。
protocol SomeProtocol 
    var mustBeSettable: Int  get set   //协议要求属性必须是可写的
    var doesNotNeedTobeSettable: Int  get    //协议要求属性可以不是可写的


protocol FullyNamed 
    var fullName: String  get 

struct Person: FullyNamed  
    var fullName: String

let john = Person(fullName: "John")
class StarShip: FullyNamed 
    var prefix: String?
    var name: String
    init(name: String, prefix: String? = nil) 
        self.name = name
        self.prefix = prefix
    
    var fullName: String   // StarShip 类遵循了 FullyNamed 协议,必须要实现协议中属性的可读属性
        return (prefix != nil ? prefix! + " " : "") + name
    

var sta = StarShip(name: "EnterPrice", prefix: "USS")
print(sta.name)
print(sta.fullName)
print(sta.prefix ?? "")

  • 在协议中定义类型属性时在前面添加 static 关键字。当类的实现使用 class 或 static 关键字前缀声明类型属性要求时,这个规则仍然适用。
protocol SomeProtocol 
    //在协议中定义类型属性时在前面添加 static 关键字
    static var someTypeProperty: Int  get 

class someClass: SomeProtocol 
    static var someTypeProperty: Int 
        return 3
    

方法要求

  • 协议可以要求采纳的类型实现指定的实例方法和类方法。这些方法作为协议定义的一部分,书写方式与正常实例和类方法的方式完全相同,但是不需要大括号和方法的主体。允许变量拥有参数,与正常的方法使用同样的规则。但在协议的定义中,方法参数不能定义默认值。
  • 正如类型属性要求的那样,当协议中定义类型方法时,你总要在其之前添加 static 关键字。即使在类 实现时,类型方法要求使用 class 或 static 作为关键字前缀,前面的规则仍然适用。

mutating 方法要求

  • 若你定义了一个协议的实例方法需求,想要异变任何采用了该协议的类型实例,只需在协议里 方法的定义当中使用 mutating 关键字。这允许结构体和枚举类型能采用相应协议并满足方法 要求。
//枚举是值类型,在值类型的实例方法中,将mutating关键字作为函数的前缀,写在func之前,表示可以在该方法中修改它所属的实例及其实例属性的值。
enum Directions: Direction 
    case north, south, east, west
    mutating func show() 
        switch self 
        case .north:
            self = .north
            print("north")
        case .south:
            self = .south
            print("south")
        case .east:
            self = .east
            print("east")
        default:
            self = .west
            print("west")
        
    

var dir = Directions.east
print(dir)


初始化器要求

  • 协议可以要求遵循协议的类型实现指定的初始化器。和一般的初始化器一样,只用将初始化器写在协议的定义当中,只是不用写大括号也就是初始化器的实体。

初始化器要求的类实现

  • 你可以通过实现指定初始化器或便捷初始化器来使遵循该协议的类满足协议的初始化器要求。 在这两种情况下,你都必须使用 required 关键字修饰初始化器的实现。

  • 如果一个子类重写了父类指定的初始化器,并且遵循协议实现了初始化器要求,那么就要为这 个初始化器的实现添加 required 和 override 两个修饰符。
protocol SomeProtocol 
    

class SomeSuperClass 
    

class someClass: SomeSuperClass, SomeProtocol 
    required override init() 
        
    
protocol TcpProtocol 
    init(no1: Int)

    func add(count: Int)

class MainClass 
    var no1: Int  //局部变量
    init(no1: Int) 
        self.no1 = no1  //初始化
    

class SubClass: MainClass, TcpProtocol 
    func add(count: Int) 
        print("lalallal")
    

    var no2: Int
    init(no1: Int, no2: Int) 
        self.no2 = no2
        super.init(no1: no1)
    
    // 因为遵循协议,需要加上"required"; 因为继承自父类,需要加上"override"
    required override convenience init(no1: Int) 
        //便利构造函数 先调用本类的指定初始化器
        self.init(no1: no1, no2: 0)
    

let res = MainClass(no1: 10)
let show = SubClass(no1: 20, no2: 30)
print("res is: \\(res.no1)")   // 10
print("res is: \\(show.no1)")   // 20
print("res is: \\(show.no2)")   // 30


将协议作为类型

  • 在函数、方法或者初始化器里作为形式参数类型或者返回类型;
  • 作为常量、变量或者属性的类型;
  • 作为数组、字典或者其他存储器的元素的类型。
//协议类型
protocol Generator 
    associatedtype member
    func next() -> member?

var items = [10, 20, 30].makeIterator()
while let x = items.next() 
    print(x)

/**  输出:
 10
 20
 30
 */

for list in [1, 2, 3].map( i in i * 5 ) 
    print(list)

/**
 输出:
 5
 10
 15
 */


协议继承

  • 协议可以继承一个或者多个其他协议并且可以在它继承的基础之上添加更多要求。协议继承的语法与类继承的语法相似,只不过可以选择列出多个继承的协议,使用逗号分隔。
protocol InheritingProtocol: SomeProtocol, AnotherProtocol 
    // 协议定义

//实例
protocol Classa 
    var no1: Int get set 
    func calculate(sum: Int)

protocol Result 
    func print(target: Classa)

class Student: Result 
    func print(target: Classa) 
        target.calculate(sum: 1)
    

class Classb: Result 
    func print(target: Classa) 
        target.calculate(sum: 5)
    

class Student2: Classa 
    var no1: Int = 10
    func calculate(sum: Int) 
        no1 -= sum
        print("学生尝试 \\(sum)次通过")
        
        if no1 <= 0 
            print("学生缺席考试")
        
    


class Player 
    var stmark: Result!
    init(stmark: Result!) 
        self.stmark = stmark
    
    func print(target: Classa) 
        stmark.print(target: target)
    

var marks = Player(stmark: Student())
var marksec = Student2()

marks.print(target: marksec)
marks.print(target: marksec)
marks.print(target: marksec)
marks.stmark = Classb()
marks.print(target: marksec)
marks.print(target: marksec)
marks.print(target: marksec)

/**输出结果:
 学生尝试 1次通过
 学生尝试 1次通过
 学生尝试 1次通过
 学生尝试 5次通过
 学生尝试 5次通过
 学生缺席考试
 学生尝试 5次通过
 学生缺席考试
*/


类专用的协议

  • 通过添加 AnyObject 关键字到协议的继承列表,你就可以限制协议只能被类类型采纳(并且不是结构体或者枚举)。
  • 可以在协议的继承列表中,通过添加class关键字,限制协议只能适配到类(class)类型。
  • 该class关键字必须是第一个出现在协议的继承列表中,其后,才是其他继承协议。格式如下:
protocol SomeProtocol 
    

protocol someClasslOnlyProtocol: AnyObject, SomeProtocol 
    // AnyObject 可以限制协议只能被类类型采纳

protocol tcpProtocol 
    init(no1: Int)

class MainClass 
    var no1: Int
    init(no1: Int) 
        self.no1 = no1
    

class SubClass: MainClass, tcpProtocol 
    var no2: Int
    init(no1: Int, no2: Int) 
        self.no2 = no2
        super.init(no1: no1)  //调用父类的指定初始化器
    
     // 因为遵循协议,需要加上"required"; 因为继承自父类,需要加上"override"
    required override convenience init(no1: Int) 
        self.init(no1: no1, no2: 0)  //便捷初始化器需要先调用本类的指定初始化器
    

let res = MainClass(no1: 20)
let show = SubClass(no1: 30, no2: 50)

print("res is: \\(res.no1)")
print("res is: \\(show.no1)")
print("res is: \\(show.no2)")
/**
 res is: 20
 res is: 30
 res is: 50
 */


协议组合

  • 可以使用协议组合来复合多个协议到一个要求里。协议组合行为就和你定义的临时局部协议一样拥有构成中所有协议的需求。协议组合不定义任何新的协议类型。
  • 协议组合使用 SomeProtocol & AnotherProtocol 的形式。你可以列举任意数量的协议,用和符号连接 ( & ),使用逗号分隔。除了协议列表,协议组合也能包含类类型,这允许你标明一个需要的父类。
//协议组合
protocol Named 
    var name: String  get 

protocol Aged 
    var age: Int  get 

struct Person: Named, Aged 
    var name: String
    var age: Int

func WishHappyBirthday(to celebrator: Named & Aged)   //使用 & 符号连接多个协议
    print("Happy birthday! \\(celebrator.name), you are \\(celebrator.age)")

let person = Person(name: "zhangsan", age: 14)
WishHappyBirthday(to: person)  // 打印: Happy birthday, zhangsan, you are 14


可选协议要求

  • 你可以给协议定义可选要求,这些要求不需要强制遵循协议的类型实现。可选要求使用 optional 修饰符作为前缀放在协议的定义中。可选要求允许你的代码与 Objective-C 操作。 协议和可选要求必须使用 @objc 标志标记。注意 @objc 协议只能被继承自 Objective-C 类或 其他 @objc 类采纳。它们不能被结构体或者枚举采纳。

以上是关于Swift 协议的主要内容,如果未能解决你的问题,请参考以下文章

视图中不可变的某些视图主体变量/不透明返回

发送包含数组的数组 json 主体(Alamofire、Swift、iOS)

Swift中什么时候不能用 () 代替 Void 来使用

Swift中什么时候不能用 () 代替 Void 来使用

8函数相关内容

是否有一个统一的 python 库来使用不同的协议传输文件