• Swift-----协议Protocol


    // 本文内容来自慕课网----玩儿转Swift

    1 协议中可以定义属性

      (1)属性不可以有默认值

      (2)必须设置是“get”还是“get set”,注意:get与set之间是没有逗号的

      (3)即使属性只有get,没有set,也不可以将属性设置为let,必须设置为var

    2 协议中可以定义方法

      (1)方法的参数不可以有默认值

    protocol Pet {
        
        // 定义属性
        var name: String {get set}
        var birthplace: String {get}    
    
        // 定义方法
        func fed(food: String)
        func playWith()
       mutating func changeName(name: String) }
     
    3 以下写法中,表示pet遵守Pet协议。由于Pet不是类,故不能用Pet()来对pet进行初始化。
    var pet: Pet 

    4 定义一个结构体实现该协议

      (1)实现协议中的属性

        (I)此时属性可以设置默认值

        (II)协议中name为可读可写的,可以直接声明为var类型就可以

        (III)协议中birthplace为可读属性,可以直接声明为let类型就可以,当然也可以声明为var类型

      (2)实现协议中的方法

        (I)可以为方法中的参数设置默认值

        (II)在结构体中,如果需要改变自身的值,需要在方法前面加mutating关键字。在协议的方法中添加mutating关键字,如果结构体来遵守协议,需要有  mutating这个关键字,如果是类来遵守协议,mutating关键字就不需要了。

    struct Dog: Pet {
    
        var name: String = "Tim"
        let birthplace: String = "Bei Jing"
        
        func fed(food: String = "Bone") {
            
            if food == "Bone" {
                print("I an happy.")
            } else {
                print("I need a bone.")
            }
        }
        
        func playWith() {
             print("Wong!")
        }
        
        mutating func changeName(name: String) {
            self.name = name
        }
    }

     5 将一个Dog类赋值给一个遵守了Pet协议的对象,是没有问题的。因为协议可以当作一个类型来看待。

    var dog: Dog = Dog()
    let aPet: Pet = dog

    6 如果只希望协议只被类class遵守,只需要在定义协议的时候在后面加上AnyObject即可。

     以下定义的Animate协议只能被类遵守,结构体是不可以遵守的。

    protocol Animate: AnyObject {
        var name: String {get set}
    }
    
    class Cat: Animate {
        
        var name: String = ""
    }

    7 Dog类在实现协议Pet的时候,如果将birthplace声明为var是没有问题的。如果birthplace被当作是Dog的属性,它是可以赋值的,但如果birthplace被作为是Pet的属性,它是不可以赋值的。

    struct Dog: Pet {
        // 其余的属性和方法省略
        var birthplace: String = "Beijing"
    }
    
    var dog: Dog = Dog()
    let aPet: Pet = dog
    
    // 对dog的birthplace属性赋值是没有问题的 dog.birthplace
    = "Shanghai"
    // 不可以对aPet的birthplace属性赋值。因为在协议Pet中,birthplace是只读的
    // aPet.birthplace = "Hangzhou"

     8 如果协议(Protocol Pet)中定义了构造函数(init),则实现协议的类(Dog)必须实现这个构造函数,而继承该类(Dog)的子类(Taidi)间接的也实现了该协议,故子类(Taidi)也必须实现这个构造函数。因此需要在类(Dog)的构造函数中填加子类(Taidi)必须实现该方法的标识,即关键字required。如果类(Dog)定义为final类,即其它类不可以继承该类,则required关键字可以省略。

    protocol Pet {
        
        var name: String {get set}
        var birthplace: String {get}
        
        // 定义构造函数
        init(name: String)
    }
    
    class Animate {
        
        var type = "mamal"
    }
    
    class Dog: Animate, Pet {
    
        var name: String = "Tim"
        var birthplace: String = "Bei Jing"
        
        required init(name: String) {
            self.name = name
        }
    }
    
    // Dog类也可以这样写
    final class Dog: Animate, Pet {
        
        var name: String = "Tim"
        var birthplace: String = "Bei Jing"
        // 此时required关键字就可以省略
        init(name: String) {
            self.name = name
        }
    }

     9 类型别名(typealias)和关联类型(associatedtype)

      类型别名(typealias)其实就是给类型重新定义一个名字。  

    extension Double {    
        var km: Double {return self * 1000.0}
        var m: Double {return self}
        var cm: Double {return self / 100}
        var ft: Double {return self / 3.28084}
    }
    let runningDistance: Double = 3.54.km
    
    // 我们可以给Double设置一个别名Length,则以上代码就可以改为下面的代码
    typealias Length = Double
    extension Double {
        var km: Length {return self * 1000.0}
        var m: Length {return self}
        var cm: Length {return self / 100}
        var ft: Length {return self / 3.28084}
    }
    let runningDistance: Length = 3.54.km

      在设计协议(protocol)时,如果有两个协议,它们的方法和属性都一样,只有协议中用到的类型不同,我们就没必要定义两个协议,只需要将这两个协议合并为一个就可以了。这时就可以在协议中使用关联类型(associatedtype),类似于类型别名(typealias)。

    protocol WeightCalaulate {
    
        associatedtype weightType
        
        var weight: weightType {get}
    }
    // 在类iphone7中,weightType为Double类型的别名
    class iphone7: WeightCalaulate {
    
        typealias weightType = Double
        
        var weight: weightType {
            return 0.114
        }
    }
    // 在类Ship中,weightType为Int类型的别名
    class Ship: WeightCalaulate {
    
        typealias weightType = Int
        
        var weight: weightType {
            return 46_328_000
        }
    }

    10 协议可以继承、可以扩展

      (1)先看下面的代码:定义了一个协议Record,两个结构体BaseballRecord、BasketballRecord,这两个结构体都实现了Record和CustomStringConvertible协议。

    protocol Record {
        var wins: Int {get}
        var loses: Int {get}
        func winningPercent() -> Double
    }
    
    struct BaseballRecord: Record, CustomStringConvertible {
        var wins: Int
        var loses: Int
        func winningPercent() -> Double {
            return Double(wins)/Double(wins + loses)
        }
        
        var description: String {
            return String(format: "WINS: %d, LOSES: %d", wins, loses)
        }
    }
    
    struct BasketballRecord: Record, CustomStringConvertible {  
        var wins: Int
        var loses: Int
        func winningPercent() -> Double {
            return Double(wins)/Double(wins + loses)
        }
    var description: String { return String(format: "WINS: %d, LOSES: %d", wins, loses) } }

      (2)可以看到,以上两个结构体都实现了Record, CustomStringConvertible两个协议,如果我们希望只要实现Record协议,就需要实现CustomStringConvertible协议。我们可以让Record协议继承自CustomStringConvertible协议,这样只要实现Record协议,就必须实现CustomStringConvertible协议。代码如下:

    // 协议中定义代码及结构体中实现的代码同上,此处省略
    protocol Record: CustomStringConvertible {
    } struct BaseballRecord: Record { } struct BasketballRecord: Record { }

      (3)可以看到,两个结构体中实现协议的代码是相同的。如果这一部分代码可以写到协议中,在结构体中就可以省去重复的代码。又协议的定义(Protocol Record)中是不可以实现代码的,我们可以通过扩展协议的方式,在扩展中实现相应的代码。(在扩展中可以进行一些默认的实现)

    protocol Record: CustomStringConvertible {
        var wins: Int {get}
        var loses: Int {get}
        func winningPercent() -> Double
    }
    
    extension Record {
      // 定义一个属性   
        var gamePlayed: Int {
            return wins + loses
        }
        // 实现Record协议中定义的方法
        func winningPercent() -> Double {
            return Double(wins)/Double(gamePlayed)
        }
        // 实现CustomStringConvertible协议
        var description: String {
            return String(format: "WINS: %d, LOSES: %d", wins, loses)
        }
    }
    struct BaseballRecord: Record { var wins: Int var loses: Int } struct BasketballRecord: Record { var wins: Int var loses: Int } let baseball = BaseballRecord(wins: 3, loses: 2) baseball.winningPercent() print(baseball) // WINS: 3, LOSES: 2

      (4)可以扩展系统中的协议

    extension CustomStringConvertible {
        var descriptionWithDate: String {
            return NSDate().description + description
        }
    }
    
    baseball.descriptionWithDate

      (5)在协议的扩展中定义的属性,在实现该协议的结构体中仍可以重写该属性。 

    struct BaseballRecord: Record {
        var wins: Int
        var loses: Int
        // 重写了协议扩展中定义的属性gamePlayed
        let gamePlayed: Int = 162
    }

      (6)用where关键字对协议做条件限定(where 类型限定)

        (I)第一一个结构体FootballRecord,它实现Record协议,并且它新增了一个属性(平局ties),这样它的gamePlayed属性以及winningPercent()方法都需要重写。代码如下:

    struct FootballRecord: Record {
        var wins: Int
        var loses: Int
        // 定义平局的属性
        var ties: Int
        
        var gamePlayed: Int {
            return wins + loses + ties
        }
        
        func winningPercent() -> Double {
            return Double(wins)/Double(gamePlayed)
        }
    }
    
    let football = FootballRecord(wins: 1, loses: 1, ties: 1)
    football.gamePlayed
    football.winningPercent()

        (II)如果按上面这样写,那么具有“平局属性”的那些结构体都需要写上面的那些全部代码。故我们可以再写一个协议Tieable,而实现了该协议Tieable的结构体的gamePlayed属性及winningPercent()方法都应该重新定义。可以对Record协议进行扩展,并且在扩展中增加限制,只有实现了Tieable协议的那些结构体才可以有这个扩展中的方法和属性。

    // 既实现了Record协议,又实现了Tieable协议的结构体(类),才可以使用这个扩展中的属性、方法
    extension Record where Self: Tieable {
        
        var gamePlayed: Int {
            return wins + loses + ties
        }
        
        func winningPercent() -> Double {
            return Double(wins)/Double(gamePlayed)
        }
    }
    struct FootballRecord: Record, Tieable {
    
        var wins: Int
        var loses: Int
    
        var ties: Int
    }
    
    let football = FootballRecord(wins: 1, loses: 1, ties: 1)
    football.gamePlayed      // 3
    football.winningPercent()  // 0.3333333333

       (7)协议聚合 

    protocol Prizable {    
        func isPrizable() -> Bool
    }
    
    // 表示Prizable和CustomStringConvertible两个协议都实现了的结构体才可以调用该方法
    func award(one: Prizable & CustomStringConvertible) {
        
    }

      (8)泛型约束(在方法定义中可以用T来代表某个协议,只需要用<T: 协议名>来定义协议就好)

    func top<T: Record>(seq: [T]) -> T {    
    }
    // 多个协议
    func top<T: Record & Prizable>(seq: [T]) -> T {
    }

      (9)可选协议:以下协议被标识为@objc属性,使得它兼容Objective-C代码。如果协议拥有可选的属性或方法时,是必须添加@objc的,因为Swift要使用Objective-C的运行时来检查类所遵守的可选方法是否存在。拥有可选方法的协议只能被类遵守,结构体和枚举是不可以遵守该协议的。

    @objc protocol Animal {
      // 注意: 在swift3中optional前面也必须有@objc @objc optional func fly() }
     
  • 相关阅读:
    A Tour of Go For continued
    A Tour of Go For
    Request对象的主要方法
    JAVA中的异常疑点解析
    用JDBC如何调用存储过程
    字节流与字符流的区别
    js判断第二个日期比第一个日期大
    JAVA中会存在内存泄露吗
    mysql 建库建表建用户
    mysql 的数据类型
  • 原文地址:https://www.cnblogs.com/muzijie/p/6596164.html
Copyright © 2020-2023  润新知