Swift学习笔记

switch语句

区间

值绑定

值绑定能在某个特定分支中吧待匹配到值绑定到本地的常量货变量上,这个常量货变量只能在该分支中使用。

...
switch statusCode {
case 100:
    ...
case let unknownCode:
    ...
}

最后一个分支,当statusCode 没有匹配到任何一个分支是,我们创建一个临时常量unknownCode,将其绑定为statusCode的值

元组和模式匹配

...
let errorCodes = (404,200)

switch errorCodes{
case (404,404):
    ...
case (404,_):
    ...
default:
    ...    
}

其中_是能匹配任何值的通配符

值绑定模式(Value-Binding Pattern)

值绑定模式是指把匹配到的值绑定给一个变量或常量。你能够在接下来的代码块中使用该变量/常量的值。把匹配到的值绑定给变量时,用关键字 var,绑定给常量时,用关键字 let。

let coordinate = (x: 1, y: 0, z: 0)
if case (let x, 0, 0) = coordinate {
  print(“x坐标为 \(x)") //  1
}

这个 case 匹配 x 坐标为任意值,y,z为0。然后名为 x 的常量绑定了元组 x 部分的值。相当于你告诉编译器,从(something,0,0)中找到一个值时,分配something给x。

如果要绑定多个值,声明多个就好了:

if case let (x, y, 0) = coordinate {
  print(“x,y分别是(\(x), \(y))") //  1, 0
}

将let移到元组外边,编译器能够绑定所有的常量。这个例子中,let 会分配到元组模式 (x, y, z) 中的各个标识符模式。因此,switch 语句中 case let (x, y ,0): 和 case (let x, let y, 0): 的匹配效果是一样的。

闭包

  • 闭包表达式语法的一般形式:闭包表达式写下花括号{} { ([parameters]) -> [return type] in //code }
let volunteersSorted =  valunteerCounts.sorted(by:{
    (i: Int, j: Int) -> Bool in
    return i < j
})
  • 如果闭包语句只有一个表达式,那么可以省略return关键字

    let volunteersSorted =  valunteerCounts.sorted(by:{
    i, j in i < j
    })
    
  • Swift 提供了快捷参数名,可以在内联闭包表达式中使用。\$0 引用第一个参数的值,\$1 引用第二个参数的值

    let volunteersSorted =  valunteerCounts.sorted(by:{
    $0 < $1
    })
    
  • 如果一个闭包是以一个函数的最后一个他参数传递的,那么它就可以在函数的圆括号以外内联

    let volunteersSorted =  valunteerCounts.sorted{
    $0 < $1
    }
    

枚举

  • 如果一个变量已经明确时某个特定的枚举类型,就可以在给变量赋值时省略枚举名
  • 在传递枚举给函数或比较枚举时可以省略枚举类型
var alignment = TexAlignment.left
alignment = .right

if alignment == .right {
    print("...")
}

关联值

关联值能让你把数据附在枚举实例上;不同的成员可以有不通类型的关联值

enum ShapeDimensions{
    // 正方形的关联值是边长
    case square(side: Double)
    // 长方形的关联值是宽和高
    case rectangle(width: Double, height: Double)
}

使用关联值

enum ShapeDimensions{
    // 正方形的关联值是边长
    case oquare(side: Double)
    // 长方形的关联值是宽和高
    case rectangle(width: Double, height: Double)
    
    func area() -> Double {
        switch self {
            case let .square(side: side):
                return side * side
            case let .rectangle(width: w, height: h):
                return w * h
        }
    }
}

在这里,switch的分支利用Swift的 模式匹配 把self的关联值绑定到新变量上。

递归枚举

swift编译器必须知道程序中每种类型的每个实例占据多少内存空间。对于递归枚举,程序无法知道需要多大的内存,从另一个角度看,需要无限内存。为解决这个问题,用关键字*indirect*告诉编译器把枚举数据放到一个指针指向的地方,而不是直接判断枚举需要多少的内存。

indirect enum FamilyTree{
    case noKnowParents
    case oneKnowParent(name: String, ancestors: FamilyTree)
    case twoKnownParents(fatherName: String, fatherAncestors: FamilyTree, motherName: String , motherAncestors: FamilyTree)
}

结构体

如果结构体的一个实例方法需要修改结构体(值类型)的属性,就必须标记为mutating

值类型不支持继承

属性

惰性存储属性 lazy

lazy属性只会在第一次访问时计算,且只会计算一次,即时里面依赖的属性发生变化也不会重新计算。 lazy属性的闭包需要在右花括号后面价空的圆括号。如果省略了圆括号,那就只是吧闭包赋给属性;有圆括号,闭包会在第一次访问属性时候执行。

struct Town{
    ...
    enum Size{
        case small
        case medium
        case large
    }
    lazy var townSize: Size={
        switch self.population {
            case 0...10_000:
                return Size.small
            case 10_001...100_000
                return Size.medium
            default:
                return Size.large
        }
    }()
}

计算属性

简单说就是 gettersetter ,其中 setter 为可选。

var townSize: Size{
    get{
        switch self.population {
            case 0...10_000:
                return Size.small
            case 10_001...100_000
                return Size.medium
            default:
                return Size.large
        }
    }
}

对比惰性属性,计算属性不需要 =() setter 可以指定新变量名,也可以不指定,不知道时默认用newValue来持有

set(newVictimPool){
    town?.population = newVictimPool
}
// 或者
set {
    town?.population =newValue
}

如果计算属性没有写入方法,就可以省略计算属性定义的get

var spookyNoise:String{
    return "Brains..."
}

属性观察者( didSet, willSet )

属性观察对于任何自定义的存储属性和任何继承的属性都可以用。自定义的计算属性不能使用观察属性。 语法类型 getter 和 setter; didSet 不指定新名字时,默认参数命名为oldValue,willSet 则为newValue

struct Town{
    var population = 5_422{
        didSet(oldPopulation){
            print("the population has changed to \(population) from \(oldPopulation).")
        }
    }
}

类型属性

值类型(结构体和枚举),类 既可以有存储类型属性,也可以有计算类型属性,跟类型方法一样。 值类型的类型属性以关键字static开头 类的类型属性可以用staticclass开头,其中static 子类不能覆盖父类的类型属性。

访问控制

swift有5个访问层级 * open * Public * internal(默认) * fileprivate * private

如果想只将读取方法变私有

private(set) var isFallingApart = false

初始化

结构体和类的存储属性在初始化完成的时候需要有初始值,即使用前必须都有值。 如果一个存储属性没有默认值,则不能使用空初始化方法

结构体初始化

结构体的默认初始化有2种形式 * 空初始化方法 var myTown = new Town() * 成员初始化方法 var myTown = new Town(population: 10_00, numberOfStopLights: 6)

类初始化

类初始化的通用语法跟值差不多,但由于类可以继承,所以还是有下面的区别 1. 类没有默认的成员初始化方法 2. 类增加了指定初始方法便捷初始化方法

指定初始化方法负责确保初始化完成前所有的属性都有值,以便实例可用。 便捷初始化方法是指定初始化方法的补充,通过调用所在类的指定初始化方法来实现,主要作用通常是为某种特殊目的创建实例。用关键字convenience标识。

类的必须初始化方法

一个类可以要求其子类提供特定的初始化方法。使用关键子required 其子类实现父类的必需初始化方法,需要加上required关键字,不要用override关键字

反初始化

只有引用类型有反初始化,值类型没有 反初始化是实例在被清出缓存前做最后的一些维护工作 类可以有多个初始化方法,但只能有一个反初始化方法 关键字deinit

deinit{
    print("Zombie named \(name) is no longer with us.")
}

可失败初始化方法

init?init!

类型转换

as!是Swift的类型转换操作符,类似于展开一个可空实例。如果试图把一个他类型转换成另一个不匹配的类型,就会触发陷阱。 as?会试图进行类型转换,如果不匹配则放回nil as会进行Swift编译器保证一定成功的转换,比如从NStringString

值类型与引用类型

在被赋予另一个实例或是作为参数传递给函数时,值类型总是被复制 Swift 的基本类型都是值类型 优先使用struct实现数据建模,必要时才用class

值类型常量的属性不能改变,即使其属性是变量(var)。即等同于常量(let)

值类型不能用同一性运算符(===)比较

协议

协议的方法默认是non-mutating,如果值类型的协议方法需要修改自身值,则需要在协议定义中,将方法标记为mutating;对于实现该协议的类则不需要在类方法中标记mutating。

错误处理

大多数时候,要抛出的错误都定义为枚举 捕获错误的结构为:do/catch。在do中至少要有一个try语句

func evaluate(_ input:String){
    let lexer = Lexer(input:input)
    
    do{
        let tokens = try lexer.lex()
    }catch {
        print("An error occurred:\(error)")
    }
}

如果do语句块中的任意一个try调用抛出错误,catch语句块就会运行,且会抛出错误 绑定到常量error上

捕获特定错误

func evaluate(_ input:String){
    ...
    do{
        ...
    }catch Lexer.Error.invalidCharacter(let character){
        ...
    }catch{
        ...
    }
}

可以在内部没有do/catch的情况下调用try,如果一个有try的调用失败,错误会被“再次抛出”

扩展

扩展能扩展结构体、枚举和类。使用关键字extension 支持扩展以下能力 * 添加计算属性 * 添加新初始化方法 * 使类型符合协议 * 添加新方法 * 添加嵌入类型 swift扩展不允许为类型添加存储属性

如果想给结构体写一个新的初始化方法,同时又不想失去成员初始化方法,那就可以用扩展给结构体添加初始化方法。

泛型

占位类型Element可以在结构体内任何一个能用具体类型的地方使用

内存管理和ARC

循环强引用 表示两个实例互相强引用对方。循环强引用就是一种内存泄漏。 弱引用weak不增加所指向实例的引用数。 弱引用有两个条件 1. 弱引用必须用var声明,不能用let 2. 弱引用必须声明为可空类型

出现循环强引用的情况有: * 两个实例互相强应用对方 * 闭包中的循环引用

非逃逸闭包 不可能产生循环强引用。不需要显示指定self 逃逸表示传递给一个函数的闭包可能会在该函数返回后被调用。 以函数参数形式声明的闭包默认是非逃逸的。