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

JerryXia 发表于 , 阅读 (0)
584d0aced8b04.png

前言

我最开始接触 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 的规范步骤

  1. 给子类单独声明的属性赋值。
  2. 调用父类的 init 方法。
  3. 改变父类声明的属性值。其它的方法调用、getter 以及 setter 也应该在这里写。

例子较长,在这里就不单独列出了。见https://developer.apple.com/library/prerelease/content/documentation/Swift/Conceptual/Swift_Programming_Language/GuidedTour.html#//apple_ref/doc/uid/TP40014097-CH2-ID1

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,例如:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    enum 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 如何实现这种协议呢?见图:
584d63a5a85e3.png

解释一点:

  • 在 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)

关注我