再读 Swift(手边笔记一) | Soledad

前言
我最开始接触 iOS 开发时是直接学的 Swift ,那时大概是 15 年的 10 月份。转眼一年多过去了,在公司实习也大半年了,工作中 OC 和 Swift 都用,但还是 OC 用的多一点。这些天打算重新捡起 Swift,直接从苹果官方的The Swift Programming Language (Swift 3.0.1)开撸。接下来的内容均为再读 Swift 时的手边笔记。目的是通过写博文的方式加深记忆以及备忘。
本文是 A Swift Tour 章节的笔记,建议有 Swift 一点点基础的人阅读。一点点就好。并且强烈建议打开 playground 一边敲一边捣鼓。
笔记
..< 和 …
使用这两个符号表示一个 range ,..< 表示开区间,… 表示闭区间。例子:
1 2 3 | for i in 0..<4 { print(i) // 输出 0,1,2,3 } |
关于隐式转换(implicitly convert)
Swift 中的值永远不会隐式转换成另一种类型。(博主心语:真尼玛太快人心。)例子:
1 2 3 | let label = "The width" let width = 94 let widthLabel = label + String(width) |
当然,你也可以 let widthLabel = "The width is \(width)"
??
?? 是处理 optional value 的一种方法,如果 optional 的值是 nil,那么使用默认值。例子:
1 2 3 | let nickName: String? = nil let fullName: String = "John Stephan" let informalGreetings = "Hi \(nickName ?? fullName)" |
在 Switch 语句中使用 let 表示 pattern
例子:
1 2 3 4 5 6 7 8 9 | let vegetable = "red pepper" switch vegetable { case "celery": print("Add some xxx") case let x where x.hasSuffix("pepper"): print("Is it a spicy \(x)?") default: print("Everything goes smoothly") } |
函数的地位
Swift 里函数是一等公民。这意味着函数可以被当成参数、返回类型等。
之前写过相关的博文,参见Swift 中的柯里化。
override 关键字
具体:
- 如果子类重写父类的方法,那么必须加上 override,不然编译器会报错。
- 如果你在子类重写的方法父类中并没有,那么编译器同样会报错。
总之:这部分你可以放心的交给编译器。(笑)
setter 中的 newValue
例子:
1 2 3 4 5 6 7 8 9 | var sideLength = 5.0 var parimeter: Double { get { return 3.0 * sideLength } set { sideLength = newValue / 3.0 } } |
你也可以给 newValue 取个你喜欢的名字。在 set 后加上括号就好。就像这样
1 2 3 | set(newParimeter) { sideLength = newParimeter / 3.0 } |
子类 init 的规范步骤
- 给子类单独声明的属性赋值。
- 调用父类的 init 方法。
- 改变父类声明的属性值。其它的方法调用、getter 以及 setter 也应该在这里写。
optional values 中 ? 的用法
如果 ? 前的值为 nil ,那么 ? 之后的 everything 都被忽略并且整个表达式的值为 nil;否则进行解包,取值。例子:
1 2 | let optionalInt: Int? = 10 let value = optionalInt?.hashValue |
枚举
- 如果你将 enum 的 rawValue 类型设置为 Int,Swift 中默认枚举类型的 rawValue 是从 0 开始的,递增值为 1。你也可以设置一个初始值,比如 5。那么之后的值都是从 5 开始算。
- enum 的 rawValue 同样可以设置成 String 以及其它浮点类型。
另外,在枚举时注意区分 associated value 和 raw value。简单的说,在定义枚举时你需要给定一个 raw value;而在创建一个具体枚举实例所关联的值就是 associated value。我们也可以不设置 raw value,例如:123456789101112enum ServerResponse {case result(String, String)case failure(String)}let success = ServerResponse.result("6:00", "8:00")switch success {case let .result(sunrise,sunset):print("Sunrise is at \(sunrise) and sunset is at \(sunset)")case let .failure(message):print("Failure ... \(message)")}
struct
struct 同样有 methods 和 initializers。它和类(class)最大的区别就是在它是值传递,而类是引用传递。例子:
另附。
protocol
关于 protocol,先看一个例子:
1 2 3 4 | protocol ExampleProtocol { var simpleDescription: String { get } // Property in protocol must have explicit { get } or { get set } specifer mutating func adjust() } |
那么,class,struct 和 enum 如何实现这种协议呢?见图:
解释一点:
- 在 struct 和 enum 的方法上加上了 mutating 表明在这个方法里改变的类的值。class 不需要 mutating 关键字,因为作为 class 中的方法始终可以改变类。
- 通过 let protocolValue:ExampleProtocol = a 的方式可以统一拿到 protocol 中声明的方法以及属性值,但是拿不到外部的值。
尽管 protocolValue 有 SimpleClass 的运行时类型,编译器还是会把它对待成 ExampleProtocol。因此你拿不到 protocolValue 在 SimpleClass 中单独声明的属性值。
extension
可以使用 extension 给现存的类型加扩展,扩展包括方法和计算属性(computed properties)。也可以在 extension 上实现协议。例如:
1 2 3 4 5 6 7 8 | extension Int: ExampleProtocol { var simpleDescription: String { return "The number \(self)" } mutating func adjust() { self += 42 } } |
Error handling
throw 和 throws
使用 throw 抛出错误,使用 throws 去标记一个函数表示它可以抛出一个错误。例如:
1 2 3 4 5 6 | func send(job: Int, toPrinter printerName: String) throws -> String { if printerName == "Never Has Toner" { throw PrinterError.noToner } return "Job sent successfully" } |
你可以声明自己的错误种类,让它实现 Error 类型就好。
1 2 3 4 5 | enum PrinterError: Error { case outOfPaper case noToner case onFire } |
do ,catch 以及 try
在 do block 中在可能抛出错误的代码前加上 try,在 catch 中对捕获的 error (默认命名)进行处理。
1 2 3 4 5 | do { let printerResponse = try send(job: 1040, toPrinter: "Epson") } catch { print(error) } |
当然,同样可以在 catch 后跟上特定的 pattern 从而匹配特定的 error。
1 2 3 4 5 6 7 8 9 10 | do { let printerResponse = try send(job: 1040, toPrinter: "Epson") print(printerResponse) } catch PrinterError.onFire { print("I'll just put this over here") } catch let printerError as PrinterError { print("Printer error: \(printerError).") } catch { print(error) } |
另外,也可以这么写。
1 2 | let printerSuccess = try? send(job: 1884, toPrinter: "Hp") // Optional("Job sent successfully") let printerFailure = try? send(job: 1885, toPrinter: "Never Has Toner") // nil |
(使用 try? 将可抛错误的代码返回一个 optional 值。如果无错误,那么为函数返回的 optional 值;如果有错误,那么返回 nil)
defer
此处先不提 defer,之后的文章中再提。
Generics
跟其它语言中的范型类似。例子:
1 2 3 4 5 6 7 8 | func makeArray<Item>(repeating item:Item, numberOfTimes: Int) -> [Item] { var result = [Item]() for _ in 0..<numberOfTimes { result.append(item) } return result } makeArray(repeating: "knock", numberOfTimes: 4) |
还有一个实现 Swift 中 OptionalValue 的例子。
1 2 3 4 5 6 | enum OptionalValue<Wrapped> { case none case some(Wrapped) } var possibleInteger: OptionalValue<Int> = .none possibleInteger = .some(100) |
关注我
- 微博:@CaiYue_
- GitHub: caiyue1993
- 邮箱:yuecai.nju@gmail.com