Protocol

  • 使用protocol定义协议
    //Protocol Syntax
    protocol SomeProtocol {
      
    }
    class SomeClass: SuperClass, FirstProtocol, SecondProtocol {
      
    }
    
  • 协议的属性条件:协议只定义属性的名字和类型,不关心是存储属性还是计算属性。同时协议定义可读或可读可写,如果定义成可读可写,那不能够实现为存储属性或者只读属性;相反,如果协议定义为可读,那么如果需要的话,可以实现为可读可写
    protocol SomeProtocol {
      var mustBeSettable: Int { get set }
      var doesNotNeedToBeSettable: Int { get }
    }
    protocol AnotherProtocol {
      static var someTypeProperty: Int { get set }
    }
    
    类似类型属性,协议中的类型属性要固定加上static,不区分类中为class其他为static
  • 协议属性条件的简单使用
    protocol FullyNamed {
      var fullName: String { get }
    }
    struct Person: FullyNamed {
      var fullName: String
    }
    let john = Person.init(fullName: "Jhon Appleseed")
    
  • 协议方法条件的简单使用
    protocol RandomNumberGenerator {
      func random() -> Double
    }
    class LinearCongruentialGenerator: RandomNumberGenerator {
      var lastRandom = 42.0
      let m = 139968.0
      let a = 3877.0
      let c = 29573.0
      func random() -> Double {
          lastRandom = (lastRandom * a + c).truncatingRemainder(dividingBy: m)
          return lastRandom / m
      }
    }
    let generator = LinearCongruentialGenerator.init()
    print("Here's a random number: \(generator.random())")
    
  • 结构体和枚举(值类型),可是使用mutating关键字修改自身的值,对于协议定义的方法同样如此。同时,在结构体和枚举的实现中也需要mutating关键字,但是类不需要
    //Mutating Method Requirements
    protocol Togglable {
      mutating func toggle()
    }
    enum OnOffSwitch: Togglable {
      case off, on
      mutating func toggle() {
          switch self {
          case .off:
              self = .on
          case .on:
              self = .off
          }
      }
    }
    var lightSwitch = OnOffSwitch.off
    lightSwitch.toggle()
    
  • 协议声明构造器方法时,需要required表示其子类也需要实现,但如果是fianal,即不会有子类时则不需要required
    protocol SomeSimpleInitializerProtocol {
      init()
    }
    class SomeSuperClass {
      init() {
          
      }
    }
    class SomeSubClass: SomeSuperClass, SomeSimpleInitializerProtocol {
      //"required" from SomeProtocol conformance; "override" from SomeSuperClass
      required override init() {
          
      }
    }
    
    
  • 协议也是一种类型,所以同样可以作为参数、返回值;作为属性、变量;作为数组、字典内容。同时注意协议作为类型,首字母大写
    //Protocols as Types
    class Dice {
      let sides: Int
      let generator: RandomNumberGenerator
      init(sides: Int, generator: RandomNumberGenerator) {
          self.sides = sides
          self.generator = generator
      }
      func roll() -> Int {
          return Int(generator.random() * Double(sides)) + 1
      }
    }
    //六面骰子,生成法则使用线性同余生成器
    var d6 = Dice.init(sides: 6, generator: LinearCongruentialGenerator())
    for _ in 1...5 {
      print("Random dice roll is \(d6.roll())")
    }
    
    
  • 代理用于使类和结构体实现另一种类型实例的某种功能,通过使用协议定义需要实现的功能,使用代理具体实现功能
    //Delegation
    //协议DiceGame可以被用于所有使用骰子的游戏
    protocol DiceGame {
      var dice: Dice { get }
      func play()
    }
    //协议DiceGameDelegate可以被用于所有使用跟踪骰子过程的类型
    protocol DiceGameDelegate {
      //提供关于DiceGame的三个代理方法,使用该DiceGameDelegate的实例可以直接调用其三个方法,而不需要关心其内部实现。创建遵循该代理的类具体实现其方法
      //类似于OC中,遵守某协议(:delegate)的类实现该方法。而定义某协议的地方调用该方法
      func gameDidStart(_ game: DiceGame)
      func game(_game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int)
      func gameDidEnd(_ game: DiceGame)
    }
    
    //使用骰子的游戏蛇与梯子
    class SnakesAndLadders: DiceGame{
      //25个格
      let finalSquare = 25
      //实现DiceGame协议,指定使用6面线性同余生成随机数的骰子
      let dice = Dice.init(sides: 6, generator: LinearCongruentialGenerator.init())
      //初始为0
      var square = 0
      //定义格子数组
      var board = [Int]()
      //初始化不同格子向上或是向下移动
      init() {
          board = Array(repeating: 0, count: finalSquare + 1)
          board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
          board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
      }
      //定义DiceGameDelegate类型代理
      //此代理非必须,定义为可选类型
      var delegate: DiceGameDelegate?
      //实现DiceGame协议,实现play方法
      func play() {
          square = 0
          //使用可选控制链避免代理为空时调用方法
          delegate?.gameDidStart(self)
          gameLoop: while square != finalSquare {
              let diceRoll = dice.roll()
              delegate?.game(_game: self, didStartNewTurnWithDiceRoll: diceRoll)
              switch square + diceRoll {
              case finalSquare:
                  break gameLoop
              case let newSquare where newSquare > finalSquare:
                  continue gameLoop
              default:
                  square += diceRoll
                  square += board[square]
              }
          }
          delegate?.gameDidEnd(self)
      }
    }
    
    class DiceGameTracker: DiceGameDelegate {
      var numberOfTurns = 0
      func gameDidStart(_ game: DiceGame) {
          numberOfTurns = 0
          if game is SnakesAndLadders {
              print("Started a new game of Snakes and Ladders")
          }
          print("The game is using a \(game.dice.sides)-sides dice")
      }
      func game(_game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) {
          numberOfTurns += 1
          print("Rolled a \(diceRoll)")
      }
      func gameDidEnd(_ game: DiceGame) {
          print("The game lasted for \(numberOfTurns) turns")
      }
    }
    
    //初始化实现DiceGameDelegate的类型实例
    let tracker = DiceGameTracker.init()
    //初始化游戏
    let game = SnakesAndLadders.init()
    //将游戏代理DiceGameDelegate类型指定为实现该代理的类型实例
    game.delegate = tracker
    //执行游戏的开始方法
    game.play()
    
  • 使用扩展加协议为现有类新增属性
    //Adding Protocol Conformance with an Extension
    protocol TextRepresentable {
      var textualDescription: String { get }
    }
    extension Dice: TextRepresentable {
      var textualDescription: String {
          return "A \(sides)-sides dice"
       }
    }
    
    使用扩展直接为类新增属性方法等,使用协议定义属性方法,然后使用拓展遵循此协议,使所有需要此协议的扩展都可以直接实现(使直接使用扩展由一步变为两步),从调理上更加清晰
  • 如果某个类型实际上遵循了某一协议,但是没有在定义该类型时声明,可以使用空的extension体来声明
    //Declaring Protocol Adoption with an Extension
    struct Hamster {
      var name: String
      var textualDescription: String {
          return "A hamster named \(name)"
      }
    }
    extension Hamster: TextRepresentable {}
    let simonTheHamster = Hamster(name: "Simon")
    let somethingTextRepresentable: TextRepresentable = simonTheHamster
    //print(somethingTextRepresentable.textualDescription)
    print(simonTheHamster.textualDescription)
    
    补充声明了Hamster实际上是遵循了TextRepresentable协议的类型
  • 协议作为类型存储于数组中,所有遵循该协议的类
    let things: [TextRepresentable] = [game, d12, simonTheHamster]
    for thing in things {
      //注意这里的thing是TextRepresentable协议类型,可以访问thing.textualDescription,但是不能访问其他的
      print(thing.textualDescription)
    }
    
    这里也就说明了上面为什么要补充说明Hamster类是遵循TextRepresentable协议的类,否则simonTheHamster存入数组中时会报类型不符的错误
  • 协议可以继承,写法类似于类的继承,不同之处在于协议可以多继承
    protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
      // protocol definition goes here
    }
    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,165评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,720评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,849评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,245评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,596评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,747评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,977评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,708评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,448评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,657评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,141评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,493评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,153评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,890评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,799评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,685评论 2 272

推荐阅读更多精彩内容