Swift笔记 - 08.可选链、协议

可选链

class Car { var price = 0 }
class Dog { var weight = 0 }
class Person {
    var name: String
    var dog: Dog = Dog()
    var car: Car? = Car()
    func age() -> Int { 18 }
    func eat() { print ("Person eat") }
    subscript (index: Int) -> Int { index }
}

var person: Person? = Person()
var agel = person!.age() // Int
var age2 = person?.age() // Int?
var name = person?.name // String?
var index = person? [6] // Int?

func getName()-> String{ "jack" }
//如果person是nil,不会调用getName()
person?.name = getName()
  • 如果可选项为nil,调用方法、下标、属性失败,结果为nil
  • 如果可选项不为nil,调用方法、下标、属性成功,结果会被包装成可选项
  • 如果结果本来就是可选项,不会进行再次包装
if let _ = person?.eat() { // ()?
    print("eat调用成功"
} else {
    print("eat调用失败"
}

var dog = person?.dog // Dog?
var weight = person?.dog.weight // Int?
var price = person?.car?.price // Int?
  • 多个?可以链接在一起
    • 如果链中任何一个节点是nil,那么整个链就会调用失败
var scores = ["Jack": [86, 82, 84], "Rose": [79, 94, 81]]
scores ["Jack"]?[0] = 100
scores ["Rose"]? [2] += 10
scores ["Kate"]?[0] = 88

var num1: Int? = 5
num1? = 10 // Optional(10)
var num2: Int? = nil
num2? = 10 // nil

var dict: [String : (Int, Int) -> Int] = [
    " sum" : (+),
    "differencell" : (-)
]
var result = dict["sum"]? (10, 20) // Optional (30), Int?

协议(Protocol)

  • 协议可以用来定义方法、属性、下标的声明,协议可以被枚举、结构体、类遵守(多个协议之间用逗号隔开)
protocol Drawable {
func draw()
var x: Int { get set }
var y: Int { get }
subscript(index :Int)->Int { get set }
}

protocol Test1 { }
protocol Test2 { }
protocol Test3 { }
class TestClass : Test1, Test2, Test3 { }
  • 协议中定义方法时不能有默认参数值
  • 默认情况下,协议中定义的内容必须全部都实现

协议中的属性

  • 协议中定义属性时必须用var关键字
  • 实现协议时的属性权限要不小于协议中定义的属性权限
  • 协议定义getset,用var存储属性或getset计算属性去实现
  • 协议定义get,用任何属性都可以实现
protocol Drawable {
    func draw()
    var x: Int { get set }
    var y: Int { get }
    subscript (index: Int) -> Int { get set }
}

class Person : Drawable {
    var x: Int = 0
    let y: Int = 0
    func draw() {
        print ("Person draw")
    }
    subscript(index: Int) -> Int {
        set { }
        get { index }
    }
}

class Person : Drawable {
    var x: Int
    {
        get {0}
        set { }
    }
    var y: Int { 0 }
    func draw() { print ("Person draw") }
    subscript(index: Int) -> Int {
        set { }
        get { index }
    }
}
  • 为了保证通用,协议中必须用static定义类型方法、类型属性、类型下标
protocol Drawable {
    static func draw()
}
class Person: Drawable {
    class func draw() {
        print("Person1 draw")
    }
}
class Person2: Drawable {
    static func draw() {
        print("Person2 draw")
    }
}
  • 只有将协议中的实例方法标记为mutating
  • 才允许结构体、枚举的具体实现修改自身内存
  • 类在实现方法时不用加mutating,枚举、结构体才需要加mutating
protocol Drawable {
    mutating func draw()
}
class Size: Drawable {
    var width: Int = 0

    func draw() {
        width = 10
    }
}
struct Point: Drawable {
    var x: Int = 10

    mutating func draw() {
        x = 10
    }
}
  • 协议中还可以定义初始化器init
  • final类实现时必须加上required
protocol Drawable {
    init(x: Int, y: Int)
}
class Point : Drawable {
    required init(x: Int, y: Int) { }
}
final class Size : Drawable {
    init(x: Int, y: Int) { }
}
  • 如果从协议实现的初始化器,刚好是重写了父类的指定初始化器
  • 那么这个初始化必须同时加required.override
protocol Livable {
    init(age: Int)
}
class Person {
    init(age: Int) { }
}
class Student : Person, Livable {
    required override init(age: Int) {
        super.init(age: age)
    }
}
  • 协议中定义的init?init!,可以用initinit?init!去实现
  • 协议中定义的init,可以用initinit!去实现
protocol Livable {
    init()
    init?(age: Int)
    init!(no: Int)
}

class Person : Livable {
    required init() { }
    // required init! 0 { }
    required init? (age: Int) { }
    // required init! (age: Int) {}
    // required init(age: Int) { }
    required init! (no: Int) { }
    // required init?(no: Int) { }
    // required init(no: Int) { }
}

协议的继承与组合

  • 一个协议可以继承其他协议
protocol Runnable {
    func run()
}
protocol Livable : Runnable {
    func breath()
}
class Person: Livable {
    func breath() { }
    func run() { }
}
  • 协议组合,可以包含1个类类型(最多1个)
protocol Livable { }
protocol Runnable { }
class Person { }

//接收Person或者其子类的实例
func fn0(obi: Person){}
//接收遵守Livable协议的实例
func fn1(obj: Livable) { }
//接收同时遵守Livable、Runnable协议的实例
func fn2(obi: Livable & Runnable){}
//接收同时遵守Livable、Runnable协议、并且是Person或者其子类的实例
func fn3(obj: Person & Livable & Runnable) {}

typealias RealPerson = Person & Livable & Runnable
//接收同时遵守Livable、Runnable协议、并且是Person或者其子类的实例
func fn4(obj: RealPerson) { }

CaseIterable协议

  • 让枚举遵守CaseIterable协议,可以实现遍历枚举值
enum Season : CaseIterable {
    case spring, summer, autumn, winter
}
let seasons = Season.allCases
print(seasons.count) // 4
for season in seasons {
    print (season)
} // spring summer autumn winter

CustomStringConvertible协议

  • 遵守Customstringconvertible协议,可以自定义实例的打印字符串
class Person: CustomStringConvertible {
    var age: Int
    var name: String

    init(age: Int, name: String) {
        self.age = age
        self.name = name
    }

    var description: String {
        "age=\(age), name=\(name)"
    }
}
var p = Person(age: 10, name: "Jack")
print(p) // age=10, name=Jack

继续阅读