zoukankan      html  css  js  c++  java
  • iOS学习笔记45-Swift(五)协议

    一、Swift协议

    协议是为方法、属性等定义一套规范,没有具体的实现,类似于Java中的抽象接口,它只是描述了方法或属性的骨架,而不是实现。方法和属性实现还需要通过定义类,函数和枚举完成。

    1. 协议定义
    //协议定义通过关键字protocol
    protocol SomeProtocol {
        //协议定义
    }
    //协议可以继承一个或者多个协议
    protocol SomeProtocol2: SomeProtocol {
        //协议定义
    }
    //结构体实现协议
    struct SomeStructure: SomeProtocol, SomeProtocol2 {
        //结构体定义
    }
    //类实现协议和继承父类,协议一般都写在父类后面
    class SomeSuperclass {
        //父类定义
    }
    class SomeClass: SomeSuperclass, SomeProtocol, SomeProtocol2 {
        //子类定义
    }
    2. 属性要求

    协议不指定是否该属性应该是一个存储属性或者计算属性,它只指定所需的属性名称和读写类型。属性要求总是声明为变量属性,用var关键字做前缀。

    protocol ClassProtocol {
       static var present: Bool { get set }  //要求该属性可读可写,并且是静态的
       var subject: String { get }    //要求该属性可读
       var stname: String { get set }   //要求该属性可读可写
    }
    //定义类来实现协议
    class MyClass: ClassProtocol {
       static var present = false  //如果没有实现协议的属性要求,会直接报错
       var subject = "Swift Protocols" //该属性设置为可读可写,也是满足协议要求的
       var stname = "Class"
       func attendance() -> String {
          return "The (self.stname) has secured 99% attendance"
       }
       func markssecured() -> String {
          return "(self.stname) has (self.subject)"
       }
    }
    //创建对象
    var classa = MyClass()
    print(classa.attendance()) //结果:The Class has secured 99% attendance
    print(classa.markssecured()) //结果:Class has Swift Protocols
    3. 普通实例方法要求

    协议可以要求指定实例方法和类型方法被一致的类型实现。这些方法被写为协议定义的一部分,跟普通实例和类型方法完全一样,但是没有大括号或方法体。可变参数是允许的,普通方法也遵循同样的规则,不过不允许给协议方法参数指定默认值。

    //定义协议,指定方法要求
    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) % m)
            return lastRandom / m
        }
    }
    let generator = LinearCongruentialGenerator()
    print("随机数: (generator.random())") //结果:随机数: 0.37464991998171
    print("另一个随机数: (generator.random())") //结果:另一个随机数: 0.729023776863283
    4. Mutating方法要求

    有时需要一个方法来修改它属于的实例。对值类型实例方法(即结构和枚举),你将mutating关键字放在方法func关键字之前,表明该方法允许修改所属实例的任何属性。这个过程描述在实例方法内修改值类型,通常用于结构体和枚举。

    protocol Togglable {
        //协议的Mutating方法要求,允许在该方法内修改值类型
        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() //此时lightSwitch变成了OnOffSwitch.On
    switch(lightSwitch) {
        case .On:
            print("开关On")
        case .Off:
            print("开关Off")
    }
    //打印:开关On
    5. 初始化构造器要求

    协议SomeProtocol中不光可以声明方法/属性/下标,还可以声明构造器,但在Swift中,除了某些特殊情况外,构造器是不被子类继承的,所以SomeClass中虽然能够保证定义了协议要求的构造器,但不能保证SomeClass的子类中也定义了协议要求的构造器。所以我们需要在实现协议要求的构造器时,使用required关键字确保SomeClass的子类必须也得实现这个构造器。

    protocol TcpProtocol {
        //初始化构造器要求
        init(aprot: Int)
    }
    class TcpClass: TcpProtocol {
        var aprot: Int
        //实现协议的初始化要求时,必须使用required关键字确保子类必须也得实现这个构造器
        required init(aprot: Int) {
            self.aprot = aprot
        }
    }
    var tcp = TcpClass(aprot: 20)
    print(tcp.aprot)
    6. 协议类型使用
    协议可以作为类型访问:
    • 函数,方法或初始化作为一个参数或返回类型
    • 常量,变量或属性
    • 数组,字典或其他容器作为项目
    //定义随机数生成器协议
    protocol RandomNumberGenerator {
        func random() -> Double
    }
    //实现RandomNumberGenerator协议的类
    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) % m)
            return lastRandom / m
        }
    }
    //定义骰子类
    class Dice {
        let sides: Int //表示「骰子」有几个面
        let generator: RandomNumberGenerator //随机数生成器
        
        //指定构造器,RandomNumberGenerator是一个协议名
        init(sides: Int, generator: RandomNumberGenerator) {
            self.sides = sides
            self.generator = generator
        }
        
        //摇动「骰子」
        func roll() -> Int {
            return Int(generator.random() * Double(sides)) + 1
        }
    }
    //创建一个6面骰子
    var dice6 = Dice(sides: 6, generator: LinearCongruentialGenerator())
    for i in 1...5 {
        print("摇动骰子:(dice6.roll())")
    }
    /*
    摇动骰子:3
    摇动骰子:5
    摇动骰子:4
    摇动骰子:5
    摇动骰子:4
    */
    7. 协议组合

    协议组合对于要求一个类型立即符合多种协议是有用的。

    protocol Named {
        var name: String { get }
    }
    protocol Aged {
        var age: Int { get }
    }
    //定义结构体实现上面2个协议
    struct Person: Named, Aged {
        var name: String
        var age: Int
    }
    //定义一个函数,接受一个符合Named和Aged协议的类型
    func wishHappyBirthday(celebrator: protocol<Named, Aged>) {
        print("(celebrator.name)(celebrator.age)岁生日快乐!")
    }
    //创建一个Person结构体,实现了Named和Aged协议
    let birthdayPerson = Person(name: "小明", age: 21)
    wishHappyBirthday(birthdayPerson) //结果:小明21岁生日快乐!
    8. 可选实现要求

    OC中协议定义的属性和变量有requiredoptionalSwift中你可以为协议定义optional要求,这些要求不需要被符合协议的类型实现。

    1. Optional协议要求只有在你的协议被@objc属性标记时指定。
    2. 即使你不与Objective-C交互,如果你希望指定optional要求,你仍然需要使用@objc标记你的协议。
    3. 使用@objc标记的协议只能通过类调用

    根据我的理解,Swift的设计理念是没有可选的协议实现概念,但是为了保持与OC兼容性,不得已支持;所以在Swift的协议中定义可选实现的前提是该协议被@objc修饰,关于@objc

    1. @objc指示该协议暴露给OC,即可以为OC代码所用
    2. @objc修饰的协议仅仅可以被类class类型遵循
    @objc protocol CounterDataSource {
        //协议可选实现的方法要求
        @optional func incrementForCount(count: Int) -> Int
        //协议可选实现的属性要求
        @optional var fixedIncrement: Int { get }
    }
    @objc class Counter {
        var count = 0
        var dataSource: CounterDataSource? //数据源属性,可选类型
        func increment() {
            //判断是否数据源有,数据源是否有实现可选的方法和属性
            if let amount = dataSource?.incrementForCount?(count) {
                count += amount
            } else if let amount = dataSource?.fixedIncrement? {
                count += amount
            }
        }
    }
    @objc class ThreeSource: CounterDataSource {
        let fixedIncrement = 3
    }
    var counter = Counter()
    counter.dataSource = ThreeSource() //设置数据源
    for i in 1...4 {
        counter.increment()
        print("(counter.count) ") //打印:3 6 9 12
    }
    1. 由于dataSource可能为nil,因此在dataSource后边加上了?标记来表明只在dataSource非空时才去调用incrementForCount方法。
    2. 即使dataSource存在,但是也无法保证其是否实现了incrementForCount方法,因此在incrementForCount方法后边也加有?标记。
  • 相关阅读:
    Android 主题theme说明 摘记
    Android开发 去掉标题栏方法 摘记
    安卓项目五子棋代码详解(二)
    关于 ake sure class name exists, is public, and has an empty constructor that is public
    百度地图3.0实现图文并茂的覆盖物
    android onSaveInstanceState()及其配对方法。
    关于集成科大讯飞语音识别的 一个问题总结
    android 关于 webview 控制其它view的显示 以及更改view数据失败的问题总结
    C# 解析 json Newtonsoft果然强大,代码写的真好
    c#数据类型 与sql的对应关系 以及 取值范围
  • 原文地址:https://www.cnblogs.com/ming1025/p/6072700.html
Copyright © 2011-2022 走看看