在 WWDC 2014 上 Apple Duang!!!
一聲發(fā)布了 Swift。在那次發(fā)布會上, Apple 向外界宣布了,自己其實(shí)是一家軟件公司。
2015 年, Apple 又 Duang!!!
一聲將 Swift 開源。這讓 Swift 迅速攻占了服務(wù)器,這使 Swift 開發(fā) Backend server 成為了可能( Vapor )。
初次接觸 Swift,讓我看到了 Scala、C#、ES 6 的影子,并且語法層面兼容 Objective C。first-class functions 特性讓語言并不局限于 OO,讓 Swift 具有函數(shù)式編程語言的表達(dá)力。 同時,Swift 作者 Chris Lattner 這位傳奇人物的存在,讓 Swift 成為值得長期投資的一門語言。
本文來自 Swift Tour,將其中的內(nèi)容歸類總結(jié),可以當(dāng)做 Cheatsheet 翻閱,對于具有經(jīng)驗(yàn)的開發(fā)者,看過這篇便可開始工作了。
變量
Swift 中可以使用 var
和 let
直接定義 變量
或 常量
。
定義變量:
?
var age = 20// 定義變量
let name = "Halo" // 定義常亮
age = 21
聲明類型:
上段代碼看起來有些類似 javascript 若類型語言的特性,Swift 本質(zhì)上還是強(qiáng)類型的語言,聲明變量的時候,可以指定數(shù)據(jù)類型:
let cash: Float = 1.25
let money: Double = 1.252525
let dollar: String = "USD"
使用 String
對于字符串,Swift 簡化了 Objective C NSString 的定義方式:@""
:
let currencies = "AUD, " + dollar
let rmb = "1 \(dollar) is 6 RMB"
let formattedString = String(format: "%.2f \(dollar) is about 600 RMB", 100.0) // 100.00 USD is about 600 RMB
\(variable)
是 Swift 為字符串模板提供的支持。<s>不過 Swift 還是支持類似 ES 6 或者 Ruby 中多行字符串模板。多行字符串還是需要 +
號拼接</s> Swift 4.2 開始支持多行字符串:
let introduceSwift = """
We are excited by this new chapter in the story of Swift.
After Apple unveiled the Swift programming language, it quickly became one of
the fastest growing languages in history. Swift makes it easy to write software
that is incredibly fast and safe by design. Now that Swift is open source, you can
help make the best general purpose programming language available everywhere.
"""
使用 tuple
tuple
是 Swift 引入的一種新的數(shù)據(jù)類型,它可以方便支持方法的多返回值:
let numbersTuple = (1, 100, 1000)
print("min: \(numbersTuple.0), max: \(numbersTuple.1), sum: \(numbersTuple.2)")
let numbersNamedTuple = (min: 1, max: 100, sum: 1000)
print("min: \(numbersNamedTuple.0), max: \(numbersNamedTuple.1), sum: \(numbersNamedTuple.2)")
print("min: \(numbersNamedTuple.min), max: \(numbersNamedTuple.max), sum: \(numbersNamedTuple.sum)")
Collections
創(chuàng)建集合:
Swift 的集合類型主要還是 Array
和 Dictionary
:
var fruits = ["apple", "banana", "oranage"]
var fruitsPrice = [
"apple": 4.25,
"banana": 6.2
]
print(fruitsPrice[fruits[1]]) // 6.2
定義空集合:
var emptyArray = [String]()
var emptyDictionary = [String: Float]()
將已有集合設(shè)置為空時,可以用如下簡寫:
fruits = []
fruitsPrice = [:]
使用 for in 遍歷集合:
for score in [1,2,3,4,5,6] {
print(score * score * score)
}
for (key, val) in ["one": 1, "two": 2] {
print("\(val) is \(key)")
}
if else 和 Optional
Swift 中新增一種類型 Optional,這個概念來自 Scala。 Optional 類似一個包裝器,它可以包裝一個數(shù)值,但是 Optional 可能返回 nil
,可能返回數(shù)據(jù)。多用于錯誤處理,通常跟 if else
連用:
聲明 Optional:
var optionalString: String? = nil
//print(optionalString!) // nil 取值會報錯
print(optionalString == nil)
var optionalName: String? = "Jian"
print(optionalName!)
String?
表示一個 String
類型的 Optional。只有 Optional 可以接受 nil
值。如果對一個 nil
的 Optional 取值會報錯,一般取值前會對 Optional 做判斷:
var optionalString: String? = nil
if optionalString != nil {
let val = optionalString!
} else {
print("There is no value")
}
if-else 簡化 Optional 取值:
Swift 為上面這種方式提供一種簡寫:
if let val = optionalString {
print("There is a value: \(val)")
} else {
print("There is no value")
}
// flatMap
optionalString.flatMap { print("There is a value: \($0)") }
通常會將 if-else 和 Optional 這么使用:
var optionalName: String? = "Jian"
print(optionalName!)
var greeting = "Hello!"
if let name = optionalName {
greeting = "Hello \(name)"
}
Optional 默認(rèn)值:
使用 ??
可以為 Optional 提供默認(rèn)值:
let fullName: String = "Jian"
let nickName: String? = nil
let displayName = "\(nickName ?? fullName)"
switch
Swift 中的 switch 強(qiáng)大很多,支持 String,多值匹配,條件匹配:
let vegetable = "red pepper"
switch vegetable {
case "celery":
print("Add some raisins and make ants on a log.")
case "cucumber", "watercress":
print("Taht would make a good tea sandwich.")
case let x where x.hasSuffix("pepper"):
print("Is it a spicy \(x)?") // Is it a spicy red pepper?
default:
print("Everythink tasts good")
}
使用循環(huán)
for in 循環(huán)
Swift 中的 for in 和 javascript 中的有點(diǎn)類似。使用 for in 便利 Dictionary:
let numbersDict = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25]
]
var largest = 0
for (kind, numbers) in numbersDict {
for number in numbers {
number
if number > largest {
largest = number
}
}
}
print(largest) // 25
使用 for in 遍歷 Range:
var total = 0
for i in 0..<4 {
total += i
}
print(total) // 6
while 循環(huán)
一般語言都會提供 while
和 do {} while
循環(huán)。Swift 采用 while
和 repeat {} while
:
var n = 1
while n < 100 {
n += n
}
print(n) //128
var m = 1
repeat {
m += m
} while m < 100
print(m) // 128
Functions
Swift 中的函數(shù)具有 javascript 和 objective c 的雙重特征。并且函數(shù)在 Swift 中是一等公民(First-class type),具有高階函數(shù)的特性。
定義函數(shù)
一般情況下,跟 Objective C 方法一樣,需要給函數(shù)參數(shù)提供 Label:
func greet(person: String, day: String) -> String {
return "Hello \(person), today is \(day)."
}
greet(person: "Jian", day: "Friday")
使用 _
表示省略 Label:
func greet(_ person: String, on day: String) -> String {
return "Hello \(person), today is \(day)."
}
greet("Jian", on: "Friday")
函數(shù)可以使用 tuple
返回多個值:
func calcStatistics(scores: [Int]) -> (min: Int, max: Int, sum: Int) {
var min = scores[0]
var max = scores[0]
var sum = 0
for score in scores {
if score > max {
max = score
} else if score < min {
min = score
}
sum += score
}
return (min, max, sum)
}
let statistics = calcStatistics(scores: [5, 1, 100, 30, 90])
print(statistics.sum) // 226
print(statistics.2) // 226
無限參數(shù):
func sumOf(numbers: Int...) -> Int {
return numbers.reduce(0, +)
}
sumOf() // 0
sumOf(numbers: 1, 2, 3, 4, 10) // 20
使用 Int...
在函數(shù)的最后一個參數(shù),表示后面可以接受無限個整數(shù)參數(shù)。
函數(shù)嵌套
func returnFifteen() -> Int {
var y = 10
func add() { y += 5 }
add()
return y
}
returnFifteen() // 15
高階函數(shù)
高階函數(shù)(high-order function),是函數(shù)是編程的一個概念,高階函數(shù)的特點(diǎn):
- 可以將函數(shù)當(dāng)做返回值,即:可以使用函數(shù)創(chuàng)建函數(shù)
- 可以將函數(shù)當(dāng)做參數(shù)接收
使用函數(shù)創(chuàng)建另一個函數(shù):
func createIncrementer() -> ((Int) -> Int) {
func plusOne(number: Int) -> Int {
return 1 + number
}
return plusOne
}
let incrementer = createIncrementer()
incrementer(10)
函數(shù)作為參數(shù):
func hasAnyMatches(_ list: [Int], matcher: (Int) -> Bool) -> Bool {
for item in list {
if matcher(item) {
return true
}
}
return false
}
func lessThanThen(number: Int) -> Bool {
return number < 10
}
hasAnyMatches([20, 10, 7, 12], matcher: lessThanThen)
Closure (閉包)
Closure 可以理解匿名函數(shù),在很多 Callback 和集合操作中使用:
hasAnyMatches([20, 10, 7, 12]) { (item: Int) -> Bool in
item < 10
}
[20, 10, 7, 12].map { (item: Int) -> Int in
return 3 * item
}
[20, 10, 7, 12].map({ item in 3 * item })
如果省略 ()
后, Closure 是唯一的參數(shù):
let sortedNumbers = [20, 10, 7, 12].sorted { $0 > $1 }
print(sortedNumbers)
Objects and Classes
定義和使用類:
class Shape {
var numberOfSides = 0
func simpleDescription() -> String {
return "A shap with \(numberOfSides) sides."
}
}
let shape = Shape()
shape.numberOfSides = 10
shape.simpleDescription()
使用繼承:
class NamedShape : Shape {
var name: String
init(name: String) {
self.name = name
}
}
// inherit and override
class Square: NamedShape {
var sideLength: Double
init(sideLength: Double, name: String) {
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 4
}
func area() -> Double {
return sideLength * sideLength
}
override func simpleDescription() -> String {
return "A square with sides of length \(sideLength)"
}
}
let square = Square(sideLength: 10.0, name: "Jian's Square")
square.area()
square.simpleDescription()
使用屬性
Swift 中對屬性的處理,有些像 C#。
使用 getter/setter
class EquilateralTriangle: NamedShape {
var sideLength: Double = 0.0
init(sideLength: Double, name: String) {
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 3
}
var perimeter: Double {
get {
return 3.0 * sideLength
}
set {
sideLength = newValue / 3.0
}
}
override func simpleDescription() -> String {
return "An equilateral triangle with sides of length \(sideLength)"
}
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "A triangle")
print(triangle.perimeter) // 9.3
triangle.perimeter = 9.9
print(triangle.sideLength) // 3.3
使用 willSet:
class TriangleAndSquare {
var triangle: EquilateralTriangle {
willSet {
square.sideLength = newValue.sideLength
}
}
var square: Square {
willSet {
triangle.sideLength = newValue.sideLength
}
}
init(size: Double, name: String) {
square = Square(sideLength: size, name: name)
triangle = EquilateralTriangle(sideLength: size, name: name)
}
}
var triangleAndSquare = TriangleAndSquare(size: 10, name: "other shape")
print(triangleAndSquare.square.sideLength) // 10.0
print(triangleAndSquare.triangle.sideLength) // 10.0
triangleAndSquare.square = Square(sideLength: 50, name: "Large square")
print(triangleAndSquare.triangle.sideLength) // 50.0
使用 willSet
保證 square
和 triangle
的 sideLength
始終相等。
使用 Optional 類型
使用 ?.
取值,當(dāng)值為 nil
時返回 nil
,而不是報錯。
let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength
let nilSquare: Square? = nil
let nilLength = nilSquare?.sideLength
Enumerations
定義 enum
類型:
enum Level: Int {
case Zero, First, Second
}
Level.Zero.rawValue
Level.First.rawValue
enum
的 rawValue
默認(rèn)從 0
開始,依次遞增。
在 enum 中創(chuàng)建方法:
enum Suit {
case spades, hearts, diamonds, clubs
func simpleDescription() -> String {
switch self {
case .spades:
return "spades"
case .hearts:
return "hearts"
case .diamonds:
return "diamonds"
case .clubs:
return "clubs"
}
}
}
定義特性類型的 enum:
enum Rank: Int {
case ace = 1
case two, three, four, five, six, seven, eight, nine, ten
case jack, queen, king
func simpleDescription() -> String {
switch self {
case .ace:
return "ace"
case .jack:
return "jack"
case .queen:
return "queen"
case .king:
return "king"
default:
return String(self.rawValue)
}
}
}
let ace = Rank.ace // ace
let aceRawValue = ace.rawValue // 1
enum Rank: Int
設(shè)置 enum
的 rawValue
為 Int
。我們也可以通過 rawValue
來創(chuàng)建 enum
:
if let convertedRank = Rank(rawValue: 3) {
let threeDescription = convertedRank.simpleDescription() // 3
}
復(fù)雜的 enum
:
enum ServerResponse {
case result(String, String)
case failure(String)
}
let success = ServerResponse.result("6:00 am", "8:00 pm")
let failure = ServerResponse.failure("Out of cheese")
switch success {
case let .result(sunrise, sunset):
print("Sunrise is at \(sunrise) and sunset is at \(sunset)") // Sunrise is at 6:00 am and sunset is at 8:00 pm
case let .failure(message):
print("Failure ... \(message)")
}
Structs
struct Card {
var rank: Rank
var suit: Suit
func simpleDescript() -> String {
return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
}
}
let threeOfSpades = Card(rank: .three, suit: .spades) // Card
print(threeOfSpades.simpleDescript()) // The 3 of spades
struct
和 class
最重要的區(qū)別是:
-
struct
傳遞值,始終傳遞 copy -
class
傳遞引用。
Protocol
protocol ExampleProtocol {
var simpleDescription: String { get }
mutating func adjust()
}
Protocol 與 Java 中的 Interface 概念類似。在 Swift 中,class、struct、enum、extension 都可以實(shí)現(xiàn) Protocol:
class 實(shí)現(xiàn) protocol:
class SimpleClass: ExampleProtocol {
var simpleDescription: String = "A simple class."
var moreProperty: Int = 315
func adjust() {
simpleDescription += " Now 100% adjusted"
}
}
var simpleClass = SimpleClass()
simpleClass.adjust()
print(simpleClass.simpleDescription) // A simple class. Now 100% adjusted
struct 實(shí)現(xiàn) protocol:
struct SimpleStructure: ExampleProtocol {
var simpleDescription: String = "A simple structure"
mutating func adjust() {
simpleDescription += " (adjusted) "
}
}
var simpleStruct = SimpleStructure()
simpleStruct.adjust()
print(simpleStruct.simpleDescription)
mutating
修飾符表示,當(dāng)前方法在 struct
中可以修改 struct 的值。
在 protocol 的應(yīng)用中,可以使用 Java 中面向 Interface 編程:
let protocolValue: ExampleProtocol = simpleStruct
print(protocolValue.simpleDescription) // A simple structure (adjusted)
Extensions
Swift 中的 extension
和 ruby 中的 Open Class 概念很像,在 ObjC 中是 Category
。它可以為已有類型添加新的特性:
extension Int: ExampleProtocol {
var simpleDescription: String {
return "The number \(self)"
}
mutating func adjust() {
self += 10
}
}
print(7.simpleDescription)
Error Handling
throws 異常
enum PointerError: Error {
case outOfPaper
case noToner
case onFire
}
// throw error
func send(job: Int, toPointer printerName: String) throws -> String {
if printerName == "Never has toner" {
throw PointerError.noToner
}
return "Job sent"
}
catch 和處理異常
對于具有 throws
聲明的方法,需要使用 try
關(guān)鍵字調(diào)用,然后使用 do {} catch {}
包裹:
do {
let pointerResp = try send(job: 1024, toPointer: "Never has toner")
print(pointerResp)
} catch { // catch all errors
print(error) // use error by default
}
處理多個異常:
do {
let pointerResponse = try send(job: 1024, toPointer: "Jian")
print(pointerResponse)
throw PointerError.onFire
} catch PointerError.onFire {
print("I'll just put shi over here, with the result of the fire.") // I'll just put shi over here, with the result of the fire.
} catch let pointerError as PointerError {
print("Printer error: \(pointerError) .")
} catch {
print(error)
}
通常 catch
多個 Error 時,catch
順序需要從小異常,到范圍更大的異常。
使用 try?
// try? -> Optional
let pointerFailure = try? send(job: 1024, toPointer: "Never has toner") // nil
let pointerSuccess = try? send(job: 2048, toPointer: "Good pointer") // Job sent
使用 defer
在方法 throws 時,會終端當(dāng)前函數(shù)后續(xù)的代碼執(zhí)行,使用 defer
可以確保 defer
代碼段中的代碼在函數(shù)返回前始終被執(zhí)行。這個概念有些像 Java 的 try {} catch {} finally {}
中的 finally
:
func ensureExecute() throws -> String {
defer {
print("Complete")
}
do {
try send(job: 1024, toPointer: "Never has toner")
} catch {
throw error
}
return "Executed"
}
let executed = try? ensureExecute() // Complete
Generic
Swift 開始支持泛型(Generic):
enum OptionalValue<Wrapped> {
case none
case some(Wrapped)
}
var possibleInteger: OptionalValue<Int> = .none // none
possibleInteger = .some(100) // some(100)
使用 where
限定泛型的類型:
func anyCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> Bool
where T.Iterator.Element: Equatable, T.Iterator.Element == U.Iterator.Element {
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
return true
}
}
}
return false
}
anyCommonElements([1,2,3,4,5], [5])
where T.Iterator.Element: Equatable, T.Iterator.Element == U.Iterator.Element
限制 T
和 U
中的元素,必須實(shí)現(xiàn) Equatable
,并且 T
和 U
中的元素是同一種類型。