1. 基礎(chǔ)
常量 && 變量
// 常量
let age = 20
// 變量
var weight = 120.4
// 類型標(biāo)識
var welcomeMessage: String
welcomeMessage = "Hello"
// 統(tǒng)一類型
var red, green, blue: Double
整數(shù)
Int:在32位平臺上是Int32,在64位平臺上是Int64
UInt:在32位平臺上是UInt32,在64位平臺上是UInt64
其他整數(shù):Int8,Int16,Int32,Int64
整數(shù)范圍
let minValue = UInt8.min // minValue is equal to 0, and is of type UInt8
let maxValue = UInt8.max // maxValue is equal to 255, and is of type UInt8
浮點(diǎn)數(shù)
Float:32位浮點(diǎn)數(shù)
Double:64位浮點(diǎn)數(shù)
類型安全和類型推導(dǎo)
Swift 是類型安全語言,在編譯期間執(zhí)行類型檢查來避免類型錯誤,但這并不意味著定義變量都要指定類型,Swift 提供類型推導(dǎo)機(jī)制,比如:
// Int 類型
var age = 20
// Double 類型
var weight = 120.5
// Double 類型
var height = 170 + 1.5
不同類型之間的數(shù)字運(yùn)算需要做顯式轉(zhuǎn)換,隱式轉(zhuǎn)換不被允許的:
let three = 3
let pointOneFourOneFiveNine = 0.14159
// 不加Double() 轉(zhuǎn)換,報錯
let pi = Double(three) + pointOneFourOneFiveNine
類型別名
typealias AudioSample = UInt16
var maxAmplitudeFound = AudioSample.min
布爾類型
let turnOn = true
if turnOn {
print("turnOn is true")
} else {
print("turnOn is false")
}
由于類型安全,Swift 中非布爾類型不能代替布爾類型使用,比如代碼報錯:
// 編譯報錯
if 1 {
print("1 不能代替 true")
}
元組
元組是一組數(shù)據(jù)的集合,可以是不同的數(shù)據(jù)類型
// 定義一個元組,類型:(Int, String)
let http404Error = (404, "Not Found")
// 獲取數(shù)據(jù)
let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)")
print("The status message is \(statusMessage)")
// 獲取部分?jǐn)?shù)據(jù)
let (justTheStatusCode, _) = http404Error
// 索引訪問
print("The status code is \(http404Error.0)")
print("The status message is \(http404Error.1)")
// 定義時提供別名
let http200Status = (statusCode: 200, description: "OK")
print("The status code is \(http200Status.statusCode)")
print("The status message is \(http200Status.description)")
Optional(可選變量)
optional 修飾的變量代表該變量可能有值,也可能沒值。沒值通過 nil 來表示
不能將非可選變量的值設(shè)置為nil
var serverResponseCode: Int? = 404
serverResponseCode = nil
// 無初始化值默認(rèn)為 nil
var surveyAnswer: String?
Swift 中的 nil 和 OC 中的 nil 不一樣,OC 中的 nil 代表是 空對象指針,用在對象類型上,Swift 的 nil 表示某個變量沒有值,所有類型都可以使用。
可選值的解包
強(qiáng)制解包
當(dāng)確認(rèn)某個變量一定有值的時候,可以使用強(qiáng)制解包符號 !
var convertedNumber: Int? = 404
if convertedNumber != nil {
print("convertedNumber has an integer value of \(convertedNumber!).")
}
可選綁定
if,while 條件語句可以使用可選綁定來解包
var convertedNumber: Int? = 404
if let actualNumber = convertedNumber {
print("The convertedNumber has an integer value of \(actualNumber)")
} else {
print("The convertedNumber has no value")
}
// 多個解包操作,有一個返回 nil 則 if 語句條件判斷失敗
if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
}
隱式解包
什么玩意?
let possibleString: String? = "An optional string."
// 需要解包使用
let forcedString: String = possibleString!
let assumedString: String! = "An implicitly unwrapped optional string."
// 不需要解包,根據(jù)類型推導(dǎo),這里會自動強(qiáng)制解包
let implicitString: String = assumedString
// 不自動解包,optionalString 是 optional 類型
let optionalString = assumedString
錯誤處理
func makeASandwich() throws {
// this function may or may not throw an error
}
do {
try makeASandwich()
eatASandwich()
} catch SandwichError.outOfCleanDishes {
washDishes()
} catch SandwichError.missingIngredients(let ingredients) {
buyGroceries(ingredients)
}
Assertions and Preconditions
斷言,他們之間的區(qū)別是 Assertions 只在 Debug 環(huán)境下生效,Preconditions 在 Debug 和 Product 環(huán)境下都生效。
let age = -3
assert(age >= 0, "A person's age can't be less than zero.")
precondition(index > 0, "Index must be greater than zero.")
基本運(yùn)算符
賦值運(yùn)算符(=)沒有返回值
默認(rèn)算術(shù)運(yùn)算符不允許值溢出
運(yùn)算符特殊操作:
// 字符串連接
"hello, " + "world" // equals "hello, world"
// 溢出操作符
let c = a &+ b
// 取余運(yùn)算
a % b
a = (b x some multiplier) + remainder
-9 % 4 // equals -1
-9 = (4 x -2) + -1
// 三元
a != nil ? a! : b 等價于 a ?? b
// 區(qū)間運(yùn)算符
// 閉區(qū)間 a...b
for index in 1...5 {
print("\(index)")
//打印 1 2 3 4 5
}
// 半開區(qū)間 a..<b
for index in 1..<5 {
print("\(index)")
//打印 1 2 3 4
}
// 單邊區(qū)間 a... or ...a
let name = [1,2,3,4,5]
for name in names[2...] {
// 區(qū)間[2-4]
print(name)
}
for name in names[...2] {
// 區(qū)間[0-2]
print(name)
}
for name in names[..<2] {
// 區(qū)間 [0-2)
print(name)
}
// 當(dāng)變量使用
let range = ...5
range.contains(7) // false
range.contains(-1) // true
2. 字符和字符串
初始化操作
var emptyString = "" // empty string literal
var anotherEmptyString = String() // initializer syntax
if emptyString.isEmpty {
print("Nothing to see here")
}
// 可變字符串
var variableString = "Horse"
variableString += " and carriage" // Horse and carriage
// 不可變字符串
let constantString = "Highlander"
constantString += " and another Highlander" // 編譯報錯
字符串字面量
// 單行
let someString = "Some string literal value"
// 多行
let quotation = """
The White Rabbit put on his spectacles. "Where shall I begin,
please your Majesty?" he asked.
"Begin at the beginning," the King said gravely, "and go on
till you come to the end; then stop."
"""
擴(kuò)展字符
// \n 不會換行
let str = #"Line 1\nLine 2"#
// \n 會換行
let str = #"Line 1\#nLine 2"#
字符操作
for character in "Dog!??" {
print(character)
}
// D o g ! ??
let exclamationMark: Character = "!"
let catCharacters: [Character] = ["C", "a", "t", "!", "??"]
字符串操作
let string1 = "hello"
let string2 = " there"
var welcome = string1 + string2
// welcome now equals "hello there"
var instruction = "look over"
instruction += string2
// instruction now equals "look over there"
// 字符串插入
let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
print(#"6 times 7 is \#(6 * 7)."#)
// Prints "6 times 7 is 42."
字符計數(shù)
// 特殊例子
var word = "cafe"
print("the number of characters in \(word) is \(word.count)")
// Prints "the number of characters in cafe is 4"
word += "\u{301}" // COMBINING ACUTE ACCENT, U+0301
print("the number of characters in \(word) is \(word.count)")
// Prints "the number of characters in café is 4"
字符訪問
let greeting = "Guten Tag!"
greeting[greeting.startIndex]
// G
greeting[greeting.index(before: greeting.endIndex)]
// !
greeting[greeting.index(after: greeting.startIndex)]
// u
let index = greeting.index(greeting.startIndex, offsetBy: 7)
greeting[index]
// a
for index in greeting.indices {
print("\(greeting[index]) ", terminator: "")
}
// Prints "G u t e n T a g ! "
插入 && 刪除
var welcome = "hello"
welcome.insert("!", at: welcome.endIndex)
// welcome now equals "hello!"
welcome.insert(contentsOf: " there", at: welcome.index(before: welcome.endIndex))
// welcome now equals "hello there!"
welcome.remove(at: welcome.index(before: welcome.endIndex))
// welcome now equals "hello there"
let range = welcome.index(welcome.endIndex, offsetBy: -6)..<welcome.endIndex
welcome.removeSubrange(range)
// welcome now equals "hello"
// substring
let greeting = "Hello, world!"
let index = greeting.firstIndex(of: ",") ?? greeting.endIndex
let beginning = greeting[..<index]
// beginning is "Hello"
// Convert the result to a String for long-term storage.
let newString = String(beginning)
3. 集合類型
Swift 提供三種集合類型,Arrays,Sets,Dictionarys。
用 var 定義的集合就是可變集合
Arrays
Arrays 存儲相同數(shù)據(jù)類型,元素有序,可重復(fù)
let a:[Any] = [1,2,"33"] 這個類型不一致是什么鬼?
var someInts = [Int]()
someInts.append(3)
someInts = []
var threeDoubles = Array(repeating: 0.0, count: 3)
// threeDoubles is of type [Double], and equals [0.0, 0.0, 0.0]
var anotherThreeDoubles = Array(repeating: 2.5, count: 3)
// anotherThreeDoubles is of type [Double], and equals [2.5, 2.5, 2.5]
var sixDoubles = threeDoubles + anotherThreeDoubles
// sixDoubles is inferred as [Double], and equals [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]
var shoppingList: [String] = ["Eggs", "Milk"]
// 類型推導(dǎo)
var shoppingList = ["Eggs", "Milk"]
if shoppingList.isEmpty {
print("The shopping list is empty.")
} else {
print("The shopping list is not empty.")
}
// 用 "Bananas", "Apples" 替換 [4-6]位置的元素
shoppingList[4...6] = ["Bananas", "Apples"]
shoppingList.insert("Maple Syrup", at: 0)
let mapleSyrup = shoppingList.remove(at: 0)
// 遍歷
for item in shoppingList {
print(item)
}
for (index, value) in shoppingList.enumerated() {
print("Item \(index + 1): \(value)")
}
Sets
Sets 存儲相同數(shù)據(jù)類型,元素?zé)o序,不可重復(fù),Sets 中的元素必須可以計算哈希值,本質(zhì)上 Sets 是通過哈希表實現(xiàn)的。
Sets 定義類型關(guān)鍵字不能省略,因為表示語法和數(shù)組類似的
var letters = Set<Character>()
letters.insert("a")
// letters now contains 1 value of type Character
letters = []
// letters is now an empty set, but is still of type Set<Character>
var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]
// 插入
favoriteGenres.insert("Jazz")
// 刪除
if let removedGenre = favoriteGenres.remove("Rock") {
print("\(removedGenre)? I'm over it.")
} else {
print("I never much cared for that.")
}
// Prints "Rock? I'm over it."
// 遍歷
for genre in favoriteGenres {
print("\(genre)")
}
// 集合操作(交集,并集等)
let oddDigits: Set = [1, 3, 5, 7, 9]
let evenDigits: Set = [0, 2, 4, 6, 8]
let singleDigitPrimeNumbers: Set = [2, 3, 5, 7]
oddDigits.union(evenDigits).sorted()
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
oddDigits.intersection(evenDigits).sorted()
// []
oddDigits.subtracting(singleDigitPrimeNumbers).sorted()
// [1, 9]
oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted()
// [1, 2, 9]
Dictionaries
Dictionaries 存儲 key-value 對
var namesOfIntegers = [Int: String]()
namesOfIntegers[16] = "sixteen"
// namesOfIntegers now contains 1 key-value pair
namesOfIntegers = [:]
// namesOfIntegers is once again an empty dictionary of type [Int: String]
var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
airports["LHR"] = "London"
if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") {
print("The old value for DUB was \(oldValue).")
}
if let airportName = airports["DUB"] {
print("The name of the airport is \(airportName).")
} else {
print("That airport is not in the airports dictionary.")
}
if let removedValue = airports.removeValue(forKey: "DUB") {
print("The removed airport's name is \(removedValue).")
} else {
print("The airports dictionary does not contain a value for DUB.")
}
// Prints "The removed airport's name is Dublin Airport."
for (airportCode, airportName) in airports {
print("\(airportCode): \(airportName)")
}
for airportCode in airports.keys {
print("Airport code: \(airportCode)")
}
// Airport code: LHR
// Airport code: YYZ
for airportName in airports.values {
print("Airport name: \(airportName)")
}
// Airport name: London Heathrow
// Airport name: Toronto Pearson
let airportCodes = [String](airports.keys)
// airportCodes is ["LHR", "YYZ"]
let airportNames = [String](airports.values)
// airportNames is ["London Heathrow", "Toronto Pearson"]
4. 控制流
For-In
for-in 可以用在序列上,比如 Arrays,Dictionarys,Sequence,Range等
let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names {
print("Hello, \(name)!")
}
let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
for (animalName, legCount) in numberOfLegs {
print("\(animalName)s have \(legCount) legs")
}
for index in 1...5 {
print("\(index) times 5 is \(index * 5)")
// 5 10 15 20 25
}
var power = 10;
for _ in 1...5 { // 忽略 index 值
power *= 2;
}
// 半開區(qū)間
let minutes = 60
for tickMark in 0..<minutes {
}
// 自定義步長
let minuteInterval = 5
for tickMark in stride(from: 0, to: minutes, by: minuteInterval) {
// [0-60) (0, 5, 10, 15 ... 45, 50, 55)
}
let hours = 12
let hourInterval = 3
for tickMark in stride(from: 3, through: hours, by: hourInterval) {
// [3-12] (3, 6, 9, 12)
}
While
var num = 10
while num > 0 {
print("循環(huán)10次")
num --;
}
repeat {
print("循環(huán)10次")
num --;
} while num > 0
條件語句
let temperatureInFahrenheit = 90
if temperatureInFahrenheit <= 32 {
print("It's very cold. Consider wearing a scarf.")
} else if temperatureInFahrenheit >= 86 {
print("It's really warm. Don't forget to wear sunscreen.")
} else {
print("It's not that cold. Wear a t-shirt.")
}
Switch
在 Swift 中,switch 語法的 case 語句不會默認(rèn)接著執(zhí)行下一個case,不需要 break 來中斷,在 switch 中使用 break 會導(dǎo)致退出整個 switch 的執(zhí)行。要實現(xiàn) C 語言中那種 case-by-case 的效果,需要使用 fallthrough 關(guān)鍵字。
let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a", "A":
print("The letter A")
fallthrough
default:
print("Not the letter A")
}
范圍匹配
let approximateCount = 62
let countedThings = "moons orbiting Saturn"
let naturalCount: String
switch approximateCount {
case 0:
naturalCount = "no"
case 1..<5:
naturalCount = "a few"
case 5..<12:
naturalCount = "several"
default:
naturalCount = "many"
}
print("There are \(naturalCount) \(countedThings).")
Tuples 匹配
let somePoint = (1, 1)
switch somePoint {
case (0, 0):
print("\(somePoint) is at the origin")
case (_, 0):
print("\(somePoint) is on the x-axis")
case (0, _):
print("\(somePoint) is on the y-axis")
case (-2...2, -2...2):
print("\(somePoint) is inside the box")
default:
print("\(somePoint) is outside of the box")
}
// Prints "(1, 1) is inside the box"
Value Bindings
let anotherPoint = (2, 0)
switch anotherPoint {
case (let x, 0):
print("on the x-axis with an x value of \(x)")
case (0, let y):
print("on the y-axis with a y value of \(y)")
case let (x, y):
print("somewhere else at (\(x), \(y))")
}
// Prints "on the x-axis with an x value of 2"
Where
let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
case let (x, y) where x == y:
print("(\(x), \(y)) is on the line x == y")
case let (x, y) where x == -y:
print("(\(x), \(y)) is on the line x == -y")
case let (x, y):
print("(\(x), \(y)) is just some arbitrary point")
}
// Prints "(1, -1) is on the line x == -y"
控制轉(zhuǎn)移
continue,break,fallthrough 略
Labeled Statements
gameLoop: while square != finalSquare {
diceRoll += 1
if diceRoll == 7 { diceRoll = 1 }
switch square + diceRoll {
case finalSquare:
break gameLoop
case let newSquare where newSquare > finalSquare:
continue gameLoop
default:
square += diceRoll
square += board[square]
}
}
print("Game over!")
guard
和 if 類似,必須提供 else 分支,暫時不明用這玩意的必要性。
func greet(person: [String: String]) {
guard let name = person["name"] else {
return
}
print("Hello \(name)!")
guard let location = person["location"] else {
print("I hope the weather is nice near you.")
return
}
print("I hope the weather is nice in \(location).")
}
API 可用檢查
if #available(iOS 10, macOS 10.12, *) {
// Use iOS 10 APIs on iOS, and use macOS 10.12 APIs on macOS
} else {
// Fall back to earlier iOS and macOS APIs
}
5.Functions
無返回值,無參數(shù)函數(shù)
func sayHelloWorld() {
print("hello, world")
}
單條語句隱式返回
func greeting(person: String) -> String {
"Hello, " + person + "!"
}
帶參數(shù),返回值,-> 是返回箭頭
func greet(_ person:String, from user: String, world: String = "Hello") -> String {
return "to:\(person), greeting from:\(user), world:\(world)"
}
greet("Json", from:"End", world: "你好");
greet("Json", from:"End");
// 返回多個值
func minMax(array: [Int]) -> (min: Int, max: Int)? {
var currentMin = array[0]
var currentMax = array[0]
for value in array[1..<array.count] {
if value < currentMin {
currentMin = value
} else if value > currentMax {
currentMax = value
}
}
return (currentMin, currentMax)
}
可變參數(shù)
func arithmeticMean(_ numbers: Double...) -> Double {
var total: Double = 0
for number in numbers {
total += number
}
return total / Double(numbers.count)
}
arithmeticMean(1, 2, 3, 4, 5)
// returns 3.0, which is the arithmetic mean of these five numbers
arithmeticMean(3, 8.25, 18.75)
// returns 10.0, which is the arithmetic mean of these three numbers
inout 參數(shù)
默認(rèn)情況,函數(shù)中的參數(shù)都是只讀,不能修改,要修改需要使用 inout 修飾
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// Prints "someInt is now 107, and anotherInt is now 3"
函數(shù)類型
var mathFunction: (Int, Int) -> Int = addTwoInts
6. 閉包
語法
{ (parameters) -> return type in
statements
}
幾個排序用法
// 數(shù)組排序
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 > s2
})
// 根據(jù)上下文類型推導(dǎo)
// 因為是 String 類型的數(shù)組調(diào)用 sort 方法
reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )
// 單表達(dá)式隱式返回
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
// 參數(shù)速記,可省略參數(shù)列表和 in 語句
reversedNames = names.sorted(by: { $0 > $1 } )
// 運(yùn)算符函數(shù),String 類型有定義 > 運(yùn)算符函數(shù)
reversedNames = names.sorted(by: >)
尾隨閉包
當(dāng)閉包作為函數(shù)最后一個參數(shù)時,可以寫成尾隨閉包
func someFunctionThatTakesAClosure(closure: () -> Void) {
// 函數(shù)體代碼
}
// 正常方式調(diào)用
someFunctionThatTakesAClosure(closure: {
// 閉包實現(xiàn)代碼
})
// 尾隨閉包調(diào)用
someFunctionThatTakesAClosure() {
// 閉包實現(xiàn)代碼
}
// 上面的排序可以修改成尾隨閉包調(diào)用
reversedNames = names.sorted() { $0 > $1 }
// 如果尾隨閉包是函數(shù)的唯一參數(shù),() 也可以省略
reversedNames = names.sorted { $0 > $1 }
變量捕捉
runningTotal 之所以會變是因為函數(shù)和閉包是引用類型
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
let incrementByTen = makeIncrementer(forIncrement: 10)
incrementByTen()
// returns a value of 10
incrementByTen()
// returns a value of 20
incrementByTen()
// returns a value of 30
逃避閉包
簡單說就是一個閉包作為參數(shù)傳入函數(shù)后,并不是在函數(shù)體內(nèi)執(zhí)行,而是在函數(shù)返回后才會在某個時機(jī)執(zhí)行,比如異步請求,這時需要把這個閉包聲明為逃逸閉包,之所以要明確是不是逃逸閉包,應(yīng)該是為了內(nèi)存優(yōu)化管理
var completionHandlers = [() -> Void]()
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}
Autoclosures
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count)
// Prints "5"
let customerProvider = { customersInLine.remove(at: 0) }
print(customersInLine.count)
// Prints "5"
print("Now serving \(customerProvider())!")
// Prints "Now serving Chris!"
print(customersInLine.count)
// Prints "4"