一、Swift發(fā)展史
- 2014.8 1.0
- 2015.9 2.0
- 2016.9 3.0
- 2017.9 4.0
- 2019.3 5.0
Swift 2.0
- Error handing 增強
- guard語法
- 協(xié)議支持?jǐn)U展
Swift 3.0
- 新的GCD和Core Graphics
- NS前綴從老的Foundation類型中移除
- 內(nèi)聯(lián)序列函數(shù)sequence
- 新增fileprivate和open兩個權(quán)限控制
- 移除了諸多棄用的特性,比如++、--運算符
Swift 4.0
- extension中可以訪問private的屬性
- 類型和協(xié)議的組合類型
- Associated Type可以追加Where約束語句
- 新的Key Paths語法
- 下標(biāo)支持泛型
- 字符串增強
Swift 5.0
- ABI穩(wěn)定
- Raw strings
- 標(biāo)準(zhǔn)庫新增Result
- 定義了與Python或Ruby等腳本語言互操作的動態(tài)可調(diào)用類型
二、Swift和OC的比較
編程范式
- Swift可以面向協(xié)議編程、函數(shù)式編程、面向?qū)ο缶幊?/li>
- OC以面向?qū)ο缶幊虨橹?,?dāng)然你可以引入類似RAC的類庫來進(jìn)行函數(shù)式編程
類型安全
- Swift是一門類型安全的語言。鼓勵程序員在代碼中清除明確值的類型。如果代碼中使用一個字符串String,那么你不能錯誤地傳遞一個整形Int給它。因為Swift是類型安全的,它會在編譯的時候做類型檢查,并且把所有不匹配的類型作為一個錯誤標(biāo)記出來。這樣使得程序員在開發(fā)中盡可能早地發(fā)現(xiàn)和修正錯誤。
- 而OC則不然,你聲明一個NSString變量,仍然可以傳一個NSNumber給它,盡管編譯器抱怨,但是你仍然可以作為NSNumber來使用它。
值類型增強
- 在Swift中,典型的有struct、enum以及tuple都是值類型。而平時使用的Int、Double、Float、String、Array、Dictionary、Set其實都是用結(jié)構(gòu)體實現(xiàn)的,也是值類型。
- OC中,NSNumber、NSString以及集合類對象都是指針類型。
枚舉增強
- Swift的枚舉可以使用整形、浮點型、字符串等,還能擁有屬性和方法,甚至支持泛型、協(xié)議、擴展等等。
- OC里面的枚舉則雞肋很多
泛型
- Swift支持泛型,也支持泛型的類型約束等特性
- 蘋果推出了Swift2.0版本,為了讓開發(fā)者從OC更好地過渡到Swift上,蘋果也為OC帶來了generics泛型支持,不過OC的泛型約束也僅停留在編譯器警告階段。
協(xié)議和擴展
- Swift對協(xié)議的支持更加豐富,配合擴展、泛型、關(guān)聯(lián)類型等可以實現(xiàn)面向協(xié)議編程,從而大大提高代碼的靈活性。同時,Swift中的protocol還可以用于值類型,如結(jié)構(gòu)體和枚舉
- OC的協(xié)議缺乏強約束,提供的optional特性往往成為很多問題的來源,而如果放棄optional又會讓實現(xiàn)代價過大
函數(shù)和閉包
- Swift中函數(shù)是一等公民,可以直接定義函數(shù)類型變量,可以作為其它函數(shù)參數(shù)傳遞,可以作為函數(shù)返回值返回
- OC里面函數(shù)仍然是次等公民,需要selector封裝或者使用block才能模擬Swift中類似的效果
三、swiftc--強大的命令行工具

llvm編譯過程.jpg

swift編譯過程.jpg
swiftc常用命令
- 生成可執(zhí)行文件
swiftc -0 main.out main.swift - Swift Abstract Syntax Tree(AST)
swiftc main.swift -dump-ast - Swift INtermediate Language(SIL)
swiftc main.swift -emit-sil - LLVM Intermediate Repressentation(LLVM IR)
swiftc main.swift -emit-ir - Assembly Language
swiftc main.swift -emit-assembly
四、REPL
- Xcode 6.1 引入了另外一種以交互式的方式來體驗Swift的方法
- Read Eval PrintLoop,簡稱REPL
swift build Build Swift packages
swift package Create and work on packages
swift run Run a program from a package
swift test Run package tests
swift repl Experiment with Swift code interactively
操作:
- 命令行中,輸入
swift repl即可進(jìn)入REPL - 退出
:quit - 幫助
:help - 將光標(biāo)移動到當(dāng)前行的開始處
control+A - 將光標(biāo)移動到當(dāng)前行的結(jié)束處
control+E
五、Playground
- Swift Playground首次公布于WWDC2016
- 最開始是為了讓人人都能愉快的學(xué)習(xí)Swift編程
- 但發(fā)展至今,這個工具越來越強大
- iPad APP Playgrounds
六、聲明變量和常量
- 使用
let來聲明常量 - 使用
var來聲明變量
類型標(biāo)注
- 在聲明一個變量或常量的時候提供類型標(biāo)注,來明確變量或常量能夠儲存值的類型
- 添加類型標(biāo)注的方法是在變量或常量的名字后面加一個冒號,再跟一個空格,最后加上要使用的類型名稱
- 可以在一行中定義多個相關(guān)的變量為相同的類型,用逗號隔開,只要在最后的變量名字后邊加上類型標(biāo)注
let a,b,c : Int
let x = 1,y = 1.1,z = "zzz"
a = 10
b = 11
c = 12
print("a = \(a),b = \(b),c = \(c)")
print("x = \(x),y = \(y),z = \(z)")
/*
a = 10,b = 11,c = 12
x = 1,y = 1.1,z = zzz
*/
OC中定義常量常用方法:1.宏定義 2.const 3.類中屬性設(shè)置為readonly(如果繼承重寫會失效)
常量和變量命名
- 常量和變量的名字幾乎可以使用任何字符,甚至包括Unicode字符
- 變量和常量的名字不能包含空白字符、數(shù)學(xué)符號、箭頭、保留的(或者無效的)Unicode碼位、連線和制表符。也不能以數(shù)字開頭,盡管數(shù)字幾乎可以使用在名字其它的地方。
打印print 和字符串插值
使用\(插值)添加字符串插值,如"a\(b)d"
七、Swift中的數(shù)值類型
整數(shù)
- Swift提供了8、16 、 32 、64位編碼的有符號和無符號整數(shù)
- 命名方式:例如8位無符號整數(shù)的類型是UInt8,32位有符號整數(shù)的類型是Int32
- 通過min和max屬性來訪問每個證書類型的最小值和最大值
OC中有char、short、int、long、NSInteger類型,分別對應(yīng)8 、 16 、32 、 64位整形,無符號類型一般為在前面加上unsigned
Swift中的Int對應(yīng)于OC的NSInteger,在32位CPU為32位,在64位CPU為64位
浮點類型
- Double:64位浮點數(shù),至少有15位數(shù)字的精度
- Float:32位浮點數(shù),至少有6位數(shù)字的精度
- 在兩種類型都可以的情況下,推薦使用Double類型
OC中除了有來自于C的float、double外,還有CGFloat,CGFloat在32位CPU為32位,在64位CPU為64位

數(shù)值范圍.jpg
Bool
- Bool: 只有兩個值,true或false
- Swift的類型安全機制會阻止你用一個非布爾量的值替換掉Bool
OC中有0為假,非0為真的概念,Swift中沒有
類型別名
- 類型別名是為一個已存在的類型定義一個可選擇的名字
- 使用關(guān)鍵字typealias定義一個類型的別名
- 當(dāng)你想通過在一個在上下文中看起來更合適可具有表達(dá)性的名字來引用一個已存在的類型時,這時別名就非常有用了
// 聲音采樣
typealias AudioSample = UInt8
let sample: AudioSample = 32
八、元組(Tuple)
- 元組可以把多個值合并成單一的復(fù)合型的值
- 元組內(nèi)的值可以是任何類型,而且可以不必是同一個類型
元素命名
- 元組中的每個元素可以指定對應(yīng)的元素名稱
- 如果沒有指定名稱的元素也可以使用下標(biāo)的方式來引用
let error = (statuCode: 404, errorMsg: "服務(wù)器在這個路徑找不到指定資源")
print(error.statuCode) //404
print(error.1) //服務(wù)器在這個路徑找不到指定資源
Tuple修改
- 用var定義的元組就是可變元組,let定義的元組就是不可變元組
- 不管是可變還是不可變元組,元組在創(chuàng)建后就不能增加和刪除元素
- 可以對可變元素的元素值進(jìn)行修改,但是不能改變其類型
- any類型的值可以賦任何類型值
Tuple分解
- 可以將一個元組的內(nèi)容分解成單獨的常量或變量
- 如果只需要使用其中的一部分?jǐn)?shù)據(jù),不需要的數(shù)據(jù)可以用下劃線
_代替
let error = (404, errorMsg: "服務(wù)器在這個路徑找不到指定資源")
let (statuCode,errorMsg) = error
print(statuCode)
print(errorMsg)
let (_,errorMsg1) = error
print(errorMsg1)
九、Optional的使用
為什么需要Optional
- OC里的nil是無類型的指針
- OC里面的數(shù)組、字典、集合等不允許放入nil
- OC所有對象變量都可以為nil
- OC只能用在對象上,而在其他地方又用其他特殊值(例如NSNotFound)表示值的缺失
Optional
可以通過給可選變量賦值一個nil來將之設(shè)置為沒有值
- 在OC中nil是一個指向不存在對象的指針
- 在Swift中,nil不是指針,它是值缺失的一種特殊類型,任何類型的可選項都可以設(shè)置成nil而不僅僅是對象類型
Optional-If語句以及強制展開
- 可選項是沒法直接使用的
- 需要用!展開之后才能使用(意思是我知道這個可選項里面肯定有值,展開吧),如果使用了!卻沒有值,會拋出異常崩潰
- 可以用if 來可選綁定接收的方式使用
let a: Int?
//a!//expression resolves to an unused variable a!
a = 10
if let a = a{
print(a)
}
Optional-綁定
- 可以使用可選綁定來判斷可選項是否包含值,如果包含就把值賦給一個臨時的常量或者變量
- 可選綁定可以與if和while的語句使用來檢查可選項內(nèi)部的值,并賦值給一個變量或常量
- 同一個if語句中包含多可選項綁定,用逗號分隔即可。如果任一可選綁定結(jié)果是nil或者布爾值為false,那么整個if判斷會被看做else
Optional-隱式展開
- 有些可選項一旦被設(shè)定值之后,就會一直擁有值,在這種情況下,就可以去掉檢查的需要,也不必每次訪問的時候都進(jìn)行展開
- 通過在聲明的類型后邊添加一個嘆號(!)而非問號(?)來書寫隱式展開可選項
- 隱式展開可選項主要被用在Swift類的初始化過程中
Optional-可選鏈
- 可選項后面加問號
- 如果可選項不為nil,返回一個可選項結(jié)果,否則返回nil
let a: String? = "abcdef"
let count = a?.count
if let count = count{
let lastIndex = count - 1
print(lastIndex) // 5
}
十、Optional的原理
Optional-實現(xiàn)探究
- Optional其實是標(biāo)準(zhǔn)庫里的一個enum類型
- 數(shù)據(jù)值其實是Optional枚舉的關(guān)聯(lián)值
- 用標(biāo)準(zhǔn)庫實現(xiàn)語言特性典型
public enum Optional<Wrapped> : ExpressibleByNilLiteral {
case none
case some(Wrapped)
@inlinable public var unsafelyUnwrapped: Wrapped { get }
}
- Optional.none就是nil
- Optional.some則包裝了實際的值
// 使用Optional枚舉來定義可選值
// 和使用String?定義的一模一樣
let a: Optional<String> = "abcdef"
- 泛型屬性
unsafelyUnwrapped,理論上我們可以直接調(diào)用unsafelyUnwrapped獲取可選項的值
let a: Optional<String> = "abcdef"
let count = a.unsafelyUnwrapped.count
print(count) // 6
十一、創(chuàng)建和初始化字符串
初始化字符串
- 字面量
- 初始化器語法
- isEmpty檢查是否為空串
var emptyString = ""
var anotherEmptyString = String()
if (emptyString.isEmpty){
print("這個字符串是空的")
}
字面量
- 字符串字面量是被雙引號("")包裹的固定順序文本字符
- Swift會為str常量推斷類型為String
let str = "a string"
多行字面量
- 多行字符串字面量是用三個雙引號引起來的一系列字符
- 多行字符串字面量把所有行包括在引號內(nèi),開始和結(jié)束默認(rèn)不會有換行符
- 當(dāng)你的代碼中在多行字符串字面量里包含了換行,那個換行符同樣會成為字符串里的值。如果你想要使用
換行符來讓你的代碼易讀,卻不想讓換行符成為字符串的值,那就在那些行的末尾使用反斜杠(\) - 要讓多行字符串字面量起始或結(jié)束于換行,就在第一或最后一行寫一個空行
- 多行字符串可以縮進(jìn)以匹配周圍的代碼。雙引號(""")前的空格會告訴Swift其他行前應(yīng)該有多少空白是需要忽略的
- 如果你在某行的空格超過了結(jié)束的雙引號("""),那么這些空格會被包含
字符串里的特殊字符
- 轉(zhuǎn)義特殊字符
\0(空字符),\\(反斜杠),\t(水平制表符),\n(換行符),\r(回車符),\"(雙引號),\'(單引號) - 任意的unicode標(biāo)量,寫作
\u{n},里面的n是一個1-8位的16進(jìn)制數(shù)字,其值是合法unicode值 - 可以在多行字符串字面量中包含雙引號(")而不需要轉(zhuǎn)義。要在多行字符串中包含文本""",轉(zhuǎn)義至少一個雙引號
擴展字符串分隔符(Raw String)
- 在字符串字面量中放置擴展分隔符來在字符串中包含特殊字符而不讓它們真的生效
- 把字符串放在雙引號(")內(nèi)并由井號(#)包裹
- 如果字符串里有"#則首尾需要兩個##
- 如果你需要字符串中某個特殊符號的效果,使用匹配你包裹的井號數(shù)量的井號并在前面寫轉(zhuǎn)義符號
\
let str = #"Line 1\nLiine 2"#
let str1 = ##"Line 1\#nLiine 2"##
let str2 = ###"Line 1\###nLiine 2"###
print(str)
print(str1)
print(str2)
/*
Line 1\nLiine 2
Line 1\#nLiine 2
Line 1
Liine 2
*/
十二、字符串的常見操作
字符串的可變性
- var 指定的可以修改
- let 指定的不可修改
- 對比OC(NSString和NSMutableString)
字符串是值類型
- String值在傳遞給方法或者函數(shù)的時候會被復(fù)制過去
- 賦值給常量或者變量的時候也是一樣
- Swift編譯器優(yōu)化了字符串使用的資源,實際上拷貝只會在確實需要的時候才進(jìn)行
操作字符
-
for-in循環(huán)遍歷String中的每一個獨立的Character - Character類型
- String值可以通過傳入Character數(shù)組來構(gòu)造
字符串插值
- 字符串插值是一種從混合常量、變量、字面量和表達(dá)式的字符串字面量構(gòu)造新String值的方法
- 每一個你插入到字符串字面量的元素都要被一對圓括號包裹,然后使用反斜杠前綴
- 類似于NSString的
stringWithFormat方法,但是更加簡便,更強大 - 可以在擴展字符串分隔符中創(chuàng)建一個包含在其他情況下會被當(dāng)做字符串插值的字符
- 要在使用擴展分隔符的字符串中使用字符串插值,在反斜杠后使用匹配首尾井號數(shù)量的井號
print(#"Write a interpolated string in Swift using \(multiplier)."#)
print(#"6 times 7 is \#(6 * 7)."#)
/*
Write a interpolated string in Swift using \(multiplier).
6 times 7 is 42.
*/
十三、如何使用索引訪問和修改字符串
字符串索引
- 每一個String值都有相關(guān)的索引類型,即
String.Index。它相當(dāng)于每個Character在字符串中的位置 - startIndex屬性來訪問String中第一個Character的位置。endIndex屬性就是String中最后一個字符后的位置
- endIndex屬性并不是字符串下標(biāo)腳本的合法實際參數(shù)
- 如果String為空,則startIndex和endIndex相等
- 使用
index(before:)和index(after:)方法來訪問給定索引的前后 - 要訪問給定索引更遠(yuǎn)的索引,你可以使用
index(_:offsetBy:)* 使用indices屬性來訪問字符串中每個字符的索引
let string = "abcdefg"
print(string[string.startIndex]) // a
//print(string[1])//報錯
print(string[string.index(before:string.endIndex)]) // g
print(string[string.index(after:string.startIndex)]) // b
print(string[string.index(string.startIndex,offsetBy:3)]) // d
print(string[string.index(string.endIndex,offsetBy:-3)]) // e
插入
- 插入字符,使用
insert(_:at:)方法 - 插入另一個字符串的內(nèi)容到特定的索引,使用
insert(contentsOf:at:)方法
var string = "abcdefg"
string.insert("x",at:string.index(string.startIndex,offsetBy:2))
print(string)//abxcdefg
string.insert(contentsOf:"12345",at:string.index(string.endIndex,offsetBy:-2))
print(string)//abxcde12345fg
刪除
- 移除字符,使用
remove(at:)方法 - 移除一小段特定范圍的字符串,使用
removeSubrange(_:)方法
var string = "abcdefg"
string.remove(at: string.index(string.startIndex,offsetBy:2))
print(string)//abdefg
string.removeSubrange(string.index(string.endIndex,offsetBy:-4)..<string.index(string.endIndex,offsetBy:-2))
print(string)//abfg
十四、子字符串、字符串比較
子字符串
- 使用下標(biāo)或者類似
prefix(_:的方法得到的子字符串是Substring類型(注意這里string的s是小寫) - Substring擁有String的大部分方法
- Substring可以通過String的構(gòu)造器轉(zhuǎn)成String類型
let string = "abcdefg"
let index = string.index(of: "e") ?? string.endIndex
let beginning = string[..<index]
let newString = String(beginning)
print(beginning) // abcd
print(beginning is String) // false
print(beginning is Substring) // true
print(newString) // abcd
print(newString is String) // true
print(newString is Substring) // false
- 子字符串重用一部分原字符串的內(nèi)存
- 修改字符串或者子字符串之前都不需要花費拷貝內(nèi)存的代價
- String和Substring都遵循StringProtocol協(xié)議,也就是說它基本上能很方便地兼容所有接受StringProtocol值的字符串操作函數(shù)
字符串比較
- 字符串和字符想等性(==和!=)
- 前綴相等性
hasPrefix(_:) - 后綴相等性
hasSuffix(_:)
let string = "abcdefg"
let string1 = "abcdefghijk"
print(string == string1) // false
print(string.hasPrefix("abcd")) // true
print(string1.hasPrefix("abcd")) // true
print(string.hasSuffix("efg")) // true
print(string1.hasSuffix("hijk")) // true