簡介
Swift是Apple在2014年6月 WWDC
發(fā)布的全新編程語言脆诉,中文名和LOGO是“雨燕”, Swift之父Chris Lattner
;
歷時5年發(fā)展棵红,從Swift1.x發(fā)展到了Swift5.x版本衷咽,經(jīng)歷了多次重大改變演熟,ABI(應(yīng)用程序二進制接口)
基于穩(wěn)定
Hello, world
print("Hello, world!")
在 Swift 語言當中景描,這一行代碼就是一個完整的程序堰乔!
- 不用編寫main函數(shù)万哪,Swift將全局范圍內(nèi)的首句可執(zhí)行代碼作為程序入口
- 一句代碼尾部可以省略分號(;)侠驯,多句代碼寫到同一行時必須用分號(;)隔開
指南
Swift 基礎(chǔ)語法
1.簡單值
使用 let來聲明一個常量抡秆,用 var來聲明一個變量, 它的值不要求在編譯時期確定,但使用之前必須賦值1次
// 常量聲明并初始化
let str = "hello,world!"
// 聲明變量,未初始化
var num:Int;
num = 10;
如果直接初始化可以不用顯示的寫類型,如果僅聲明未初始化就需要添加類型
2. 類型標注
在聲明一個變量或常量的時候提供類型標注吟策,來明確變量或常量能夠儲存值的類型;
var num: Int = 100
3. 數(shù)據(jù)類型
- 整數(shù)類型:Int8儒士、Int16、Int32踊挠、Int64乍桂、UInt8、UInt16效床、UInt32睹酌、UInt64
- 在32bit平臺,Int等價于Int32剩檀, 64bit 平臺: Int等價于Int64
- 整數(shù)的最值:UInt8.max憋沿、Int16.min
- 一般情況下,都是直接使用Int即可
- 浮點類型:Float沪猴,32位辐啄,精度只有6位;Double,64位运嗜,精度至少15位
字面量
// 布爾值
let bool = true
// 字符串
let str1 = "hello,world"
// 字符
let characher:Character = ""
// 整數(shù)
let intDeciaml = 17 // 十進制
let intBinary = 0b10001 // 二進制
let intOctal = 0o21 //八進制
let intHexadecimal = 0x11 //十六進制
// 浮點型
let doubleDecimal = 125.0 // 十進制 125.0等價于 1.25e2 1.25e2 (e2)表示10的二次方. 1.25*10^2
// 數(shù)組
let array = [1,2,3,4,5,6,7,8,9.10]
// 字典
let dict = ["age":18, "height":100, "width":200]
5. 元組
// 定義元組,通過.0 或 .1來訪問內(nèi)部屬性
let http404Error = (404,"網(wǎng)頁不存在")
print(http404Error.0, http404Error.1)
// 定義元組,分別給元組內(nèi)參數(shù)賦值,通過參數(shù)進行訪問
let (statusCode, errorString) = (404,"網(wǎng)頁不存在")
print(statusCode, errorString)
// 使用_,表示不賦值
let (statusCode1, _) = (404,"網(wǎng)頁不存在")
print(statusCode1)
// 通過元組內(nèi)部參數(shù)進行訪問
let http200Status = (statusCode:200, statusString:"請求成功")
print(http200Status.statusCode, http200Status.statusString)
Swift 流程控制
1. if-else
- if 后面的條件只能是Bool類型, if 后面括號可以省略
var age : Int = 30
if age == 30 {
print("my age is \(age)")
}
2. while
- while 后面需要時 bool 類型
- repeat-while 等同于 do-while
var count = 0
while count<=5 {
print("num is \(count)")
count += 1
}
count = 0
repeat {
print("num is \(count)")
}while count>0
3. for-in
區(qū)間運算
- 閉區(qū)間運算符:a...b ,表示 a<= 取值 <=b
- 半開區(qū)間運算符:a..<b壶辜, a <= 取值 < b
- 單側(cè)區(qū)間:讓區(qū)間朝一個方向盡可能的遠 a..., a 到無限大
// 區(qū)間類型
let rang1: ClosedRange<Int> = 1...3
let rang2: Range<Int> = 1..<3
let rang3: PartialRangeThrough<Int> = ...3
for-in常見寫法
// 方式一: 默認num是let 類型,可以省略不寫
for num in 1...10{
print(num)
}
// 方式二: 如果要修改 num 的值,需要更改為變量
for var num in 1...10 {
num += 100
print(num)
}
// 方式三: 去過不需要用到 num 可以使用_缺省
for _ in 1...10 {
print("hello,world")
}
- 區(qū)間運算符應(yīng)用
// 設(shè)置數(shù)組的取值范圍
let arr2 = ["my", "name", "is", "Alex"]
for str in arr2[1...2] {
print(str)
}
// 單側(cè)區(qū)間運算符
for str in arr2[2...] {
print(str)
}
- 帶間隔的區(qū)間值, 使用 stride來進行間隔設(shè)置
// num 的取值:從50開始,累加10担租,不超過100
let number = 10
for num in stride(from: 50, to: 100, by: number) {
print(num)
}
4. Switch
switch 與普通 switch 使用類似
注意點:
- case砸民、default后面不能寫大括號{}
- 使用fallthrough可以實現(xiàn)貫穿效果
- switch必須要保證能處理所有情況
- case、default后面至少要有一條語句 ;如果不想做任何事奋救,加個break即可
- 如果能保證已處理所有情況岭参,也可以不必使用default
- switch也支持Character、String類型
enum Answer { case right, wrong }
let answer = Answer.right
switch answer {
case .right:
print("right")
case .wrong:
print("wrong")
}
- 復(fù)合條件
let name = "chuan"
switch name {
// 使用, 分割,可以同時判斷多個條件,滿足一個即執(zhí)行
case "chuan", "bing":
print("chuan/bing")
default:
break
}
- 區(qū)間匹配尝艘、元組匹配
// 區(qū)間匹配
let count_num = 99
switch count_num {
case 1...10:
print("1...10")
case 11...20:
print("11...20")
case 21..<30:
print("21..<30")
case 30...:
print(count_num)
default:
break
}
// 元組匹配
let point = (1, 1)
switch point {
case (0, 0):
print("the origin")
case (_, 0):
print("on the x-axis")
case (0, _):
print("on the y-axis")
case (-2...2, -2...2):
print("inside the box")
default:
print("outside of the box")
}
- 值綁定,如果有一個值相同,另外一個則會進行綁定
// 值綁定
let point1 = (1, 1)
switch point1 {
case (let x, 0):
print("the origin is\(x)")
case (let x, let y):
print("x is \(x), y is \(y)")
case let (x, y):
print("xxxx is \(x), yyyy is \(y)")
default:
print("outside of the box")
}
5. where
where用于判斷某個條件滿足才會進行執(zhí)行
// for
for num in 1...100 where num % 10 == 0 {
print(num)
}
// switch
let point2 = (1, 2)
switch point2 {
case let (x, y) where x == y:
print("x is \(x) y is \(y)")
case let (x, y) where x != y:
print("xx is \(x) yy is \(y)")
default:
break
}
Swift 函數(shù)
1. 定義和調(diào)用函數(shù)
- func的為函數(shù)關(guān)鍵字前綴, ->表示 函數(shù)返回的類型
func greet(person: String) -> String{
let greeting = "hello" + person + "!"
return greeting
}
greet(person: "Alex")
2. 隱式返回
- 如果整個函數(shù)體是一個單一表達式演侯,那么函數(shù)會隱式返回這個表達式
func sum(v1: Int, v2: Int) -> Int {
v1 + v2
}
sum(v1: 10, v2: 20)
3. 返回元組:實現(xiàn)多返回值
func calculate(v1 : Int, v2: Int) -> (sum: Int, subtract: Int, average: Int){
let sum = v1 + v2;
let subtract = v1 - v2;
let average = sum / 2
return (sum, subtract, average)
}
calculate(v1: 20, v2: 10)
4. 參數(shù)標簽
- 可以修改參數(shù)標簽,方便閱讀
func gotowork(at time: String){
print("this time is \(time)")
}
gotowork(at: "08:00")
- 可以使用下劃線_ 省略參數(shù)標簽
func sum(_ v1: Int, _ v2: Int) -> Int {
return v1 + v2;
}
let result = sum(10, 20)
5. 默認參數(shù)值
- 參數(shù)可以有默認值
func check(name: String = "nobody", age: Int, job: String = "none") {
print("name=\(name), age=\(age), job=\(job)")
}
// 2種調(diào)用方式
check(age: 10)
check(name: "alex", age: 30, job: "it")
6. 可變參數(shù)
- 一個參數(shù)可以傳入多個值
func sum(_ numbers: Int...) -> Int {
var total = 0
for number in numbers {
total += number
}
return total
}
sum(10, 20, 30, 40) // 100
- 一個函數(shù)最多只能有1個可變參數(shù),緊跟在可變參數(shù)后面的參數(shù)不能省略參數(shù)標簽
// 參數(shù)string不能省略標簽
func test(_ numbers: Int..., string: String, _ other: String) { }
test(10, 20, 30, string: "Jack", "Rose")
7. 輸入輸出參數(shù)(In-Out Parameter)
可以用inout定義一個輸入輸出參數(shù):可以在函數(shù)內(nèi)部修改外部實參的值
- 可變參數(shù)不能標記為inout
- inout參數(shù)不能有默認值
- inout參數(shù)的本質(zhì)是地址傳遞(引用傳遞) n inout參數(shù)只能傳入可以被多次賦值的
func swapValues(v1: inout Int, v2: inout Int) {
let tmp = v1
v1 = v2
v2 = tmp
}
var num1 = 10
var num2 = 20
swapValues(v1: &num1, v2: &num2)
8. 函數(shù)重載(Function Overload)
- 函數(shù)名相同, 參數(shù)個數(shù)不同 || 參數(shù)類型不同 || 參數(shù)標簽不同
- 返回值類型與函數(shù)重載無關(guān)
func sum(v1: Int, v2: Int) -> Int {
v1 + v2
}
func sum(v1: Int, v2: Int, v3: Int) -> Int {
v1 + v2 + v3
} // 參數(shù)個數(shù)不同
func sum(v1: Int, v2: Double) -> Double {
Double(v1) + v2
} // 參數(shù)類型不同
func sum(_ v1: Int, _ v2: Int) -> Int {
v1 + v2
} // 參數(shù)標簽不同
9. 內(nèi)聯(lián)函數(shù)(Inline Function)
如果開啟了編譯器優(yōu)化(Release模式默認會開啟優(yōu)化),編譯器會自動將某些函數(shù)變成內(nèi)聯(lián)函數(shù)
- 將函數(shù)調(diào)用展開成函數(shù)體
哪些函數(shù)不會被內(nèi)聯(lián)?
- 函數(shù)體比較長
- 包含遞歸調(diào)用
- 包含動態(tài)派發(fā)
10. 函數(shù)類型(Function Type)
- 每一個函數(shù)都是有類型的背亥,函數(shù)類型由形式參數(shù)類型秒际、返回值類型組成
func test() { } // () -> Void 或者 () -> ()
func sum(a: Int, b: Int) -> Int {
a+b
} // (Int, Int) -> Int
// 定義變量
var fn: (Int, Int) -> Int = sum
fn(2, 3) // 5,調(diào)用時不需要參數(shù)標簽
- 函數(shù)類型作為函數(shù)參數(shù)
func sum(v1: Int, v2: Int) -> Int {
v1 + v2
}
func difference(v1: Int, v2: Int) -> Int {
v1 - v2
}
func printResult(_ mathFn: (Int, Int) -> Int, _ a: Int, _ b: Int)
{
print("Result: \(mathFn(a, b))")
}
printResult(sum, 5, 2) // Result: 7
printResult(difference, 5, 2) // Result: 3
- 函數(shù)類型作為函數(shù)返回值
func next(_ input: Int) -> Int {
input + 1
}
func previous(_ input: Int) -> Int {
input - 1 }
func forward(_ forward: Bool) -> (Int) -> Int {
forward ? next : previous
}
forward(true)(3) // 4
forward(false)(3) // 2
11. typealias
- typealias用來給類型起別名
typealias Byte = Int8
typealias Short = Int16
typealias Long = Int64
typealias Date = (year: Int, month: Int, day: Int)
func test(_ date: Date) {
print(date.0)
print(date.year)
}
test((2011, 9, 10))
typealias IntFn = (Int, Int) -> Int
func difference(v1: Int, v2: Int) -> Int {
v1 - v2
}
let fn: IntFn = difference
fn(20, 10) // 10
func setFn(_ fn: IntFn) { }
setFn(difference)
func getFn() -> IntFn { difference }
按照Swift標準庫的定義隘梨,Void就是空元組()
12嵌套函數(shù)(Nested Function)
- 將函數(shù)定義在函數(shù)內(nèi)部
func forward(_ forward: Bool) -> (Int) -> Int {
func next(_ input: Int) -> Int {
input + 1
}
func previous(_ input: Int) -> Int {
input - 1
}
return forward ? next : previous
}
forward(true)(3) // 4
forward(false)(3) // 2
Swift 閉包
在Swift中程癌,可以通過func定義一個函數(shù),也可以通過閉包表達式定義一個函數(shù)
1 閉包表達式
- 閉包表達式
{
(參數(shù)列表) -> 返回值類型 in 函數(shù)體代碼
}
- 閉包表達式的簡寫
// 定義函數(shù),并引入閉包表達式
func exec(v1: Int, v2: Int, fn: (Int, Int) -> Int) {
print(fn(v1, v2))
}
// 正常寫法
exec(v1: 10, v2: 20, fn: {
(v1: Int, v2: Int) -> Int in
return v1 + v2
})
// 從語境中推斷類型,推斷閉包參數(shù)類型
exec(v1: 10, v2: 20, fn: {
v1, v2 in return v1 + v2
})
// 從單表達式閉包隱式返回,刪掉 return 關(guān)鍵字來隱式返回它們單個表達式的結(jié)果
exec(v1: 10, v2: 20, fn: {
v1, v2 in v1 + v2
})
// 簡寫的實際參數(shù)名,動對行內(nèi)閉包提供簡寫實際參數(shù)名轴猎,你也可以通過 $0 , $1 , $2 等名字來引用閉包的實際參數(shù)值嵌莉。
exec(v1: 10, v2: 20, fn: { $0 + $1 })
// 運算符函數(shù)
exec(v1: 10, v2: 20, fn: +)
2 尾隨閉包
如果將一個很長的閉包表達式作為函數(shù)的最后一個實參,使用尾隨閉包可以增強函數(shù)的可讀性
- 尾隨閉包是一個被書寫在函數(shù)調(diào)用括號外面(后面)的閉包表達式
func exec(v1: Int, v2: Int, fn: (Int, Int) -> Int) {
print(fn(v1, v2))
}
exec(v1: 10, v2: 20) {
$0 + $1
}
- 如果閉包表達式是函數(shù)的唯一實參捻脖,而且使用了尾隨閉包的語法锐峭,那就不需要在函數(shù)名后邊寫圓括號
func exec(fn: (Int, Int) -> Int) {
print(fn(1, 2))
}
exec(fn: { $0 + $1 })
exec() { $0 + $1 }
exec { $0 + $1 }
3. 逃逸閉包
當閉包作為一個實際參數(shù)傳遞給一個函數(shù)的時候中鼠,我們就說這個閉包逃逸了,可以在形式參數(shù)前寫 @escaping
來明確閉包是允許逃逸的沿癞。
閉包可以逃逸的一種方法是被儲存在定義于函數(shù)外的變量里援雇。比如說,很多函數(shù)接收閉包實際參數(shù)來作為啟動異步任務(wù)的回調(diào)椎扬。函數(shù)在啟動任務(wù)后返回惫搏,但是閉包要直到任務(wù)完成——閉包需要逃逸,以便于稍后調(diào)用蚕涤。
// 定義一個數(shù)組用于存儲閉包類型
var completionHandlers: [() -> Void] = []
// 在方法中將閉包當做實際參數(shù),存儲到外部變量中
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}
如果你不標記函數(shù)的形式參數(shù)為 @escaping 筐赔,你就會遇到編譯時錯誤。
4. 自動閉包
自動閉包是一種自動創(chuàng)建的用來把作為實際參數(shù)傳遞給函數(shù)的表達式打包的閉包揖铜。它不接受任何實際參數(shù)茴丰,并且當它被調(diào)用時,它會返回內(nèi)部打包的表達式的值天吓。這個語法的好處在于通過寫普通表達式代替顯式閉包而使你省略包圍函數(shù)形式參數(shù)的括號贿肩。
func getFirstPositive(_ v1: Int, _ v2: @autoclosure () -> Int) -> Int? {
return v1 > 0 ? v1 : v2()
}
getFirstPositive(10, 20)
- 為了避免與期望沖突,使用了@autoclosure的地方最好明確注釋清楚:這個值會被推遲執(zhí)行
- @autoclosure 會自動將 20 封裝成閉包 { 20 }
- @autoclosure 只支持 () -> T 格式的參數(shù)
- @autoclosure 并非只支持最后1個參數(shù)
- 有@autoclosure龄寞、無@autoclosure汰规,構(gòu)成了函數(shù)重載
- 如果你想要自動閉包允許逃逸,就同時使用 @autoclosure 和 @escaping 標志物邑。
Swfit 枚舉
枚舉為一組相關(guān)值定義了一個通用類型控轿,從而可以讓你在代碼中類型安全地操作這些值。
1. 枚舉的基本用法
enum RequestType{
case get
case post
case put
case delete
}
2. 關(guān)聯(lián)值(Associated Values)
有時會將枚舉的成員值跟其他類型的關(guān)聯(lián)存儲在一起拂封,會非常有用
enum Date {
case digit(year: Int, month: Int, day: Int)
case string(String)
}
var date = Date.digit(year: 2011, month: 9, day: 10)
date = .string("2011-09-10")
switch date {
case .digit(let year, let month, let day):
print(year, month, day)
case let .string(value):
print(value)
}
3. 原始值(Raw Values)
枚舉成員可以使用相同類型的默認值預(yù)先關(guān)聯(lián),這個默認值叫做:原始值
// 原始值,不寫默認是枚舉值
enum Grade: String {
case perfect = "A"
case great = "B"
case good = "C"
case bad = "D"
}
print(Grade.perfect)
print(Grade.perfect.rawValue)
4. 隱式原始值(Implicitly Assigned Raw Values)
如果枚舉的原始值類型是Int鹦蠕、String冒签,Swift會自動分配原始值
enum Season : Int {
case spring, summer, autumn, winter
}
print(Season.spring.rawValue) // 0
print(Season.summer.rawValue) // 1
print(Season.autumn.rawValue) // 2
print(Season.winter.rawValue) // 3
5. 遞歸枚舉(Recursive Enumeration)
枚舉值包含自己枚舉,需要使用 indirect
關(guān)鍵字
indirect enum ArithExpr {
case number(Int)
case sum(ArithExpr, ArithExpr)
case difference(ArithExpr, ArithExpr)
}
let five = ArithExpr.number(5)
let four = ArithExpr.number(4)
let two = ArithExpr.number(2)
let sum = ArithExpr.sum(five, four)
let difference = ArithExpr.difference(sum, two)
6. MemoryLayout
可以使用MemoryLayout獲取數(shù)據(jù)類型占用的內(nèi)存大小
enum Password {
case number(Int, Int, Int, Int)
case other
}
MemoryLayout<Password>.stride // 40, 分配占用的空間大小 MemoryLayout<Password>.size // 33, 實際用到的空間大小 MemoryLayout<Password>.alignment // 8, 對齊參數(shù)
Swift 可選項
可選項
可選項,一般也叫可選類型钟病,它允許將值設(shè)置為nil
在類型名稱后面加個問號? 來定義一個可選項
1. 可選項(Optional)
實例
var name: String? = "Jack"
name = nil
var age: Int? // 默認就是nil age = 10
age = nil
2.強制解包(Forced Unwrapping)
可選項是對其他類型的一層包裝萧恕,可以將它理解為一個盒子
- 如果為nil,那么它是個空盒子
- 如果不為nil肠阱,那么盒子里裝的是:被包裝類型的數(shù)據(jù)
- 如果要從可選項中取出被包裝的數(shù)據(jù)(將盒子里裝的東西取出來)票唆,需要使用感嘆號! 進行強制解包
var age: Int? = 10
let ageInt: Int = age!
- 如果對值為nil的可選項(空盒子)進行強制解包,將會產(chǎn)生運行時錯誤
var age: Int?
age!
Fatal error: Unexpectedly found nil while unwrapping an Optional value
3.可選項綁定(Optional Binding)
可以使用可選項綁定來判斷可選項是否包含值
- 如果包含就自動解包屹徘,把值賦給一個臨時的常量(let)或者變量(var)走趋,并返回true,否則返回false
if let number = Int("123") {
print("字符串轉(zhuǎn)換整數(shù)成功:\(number)") // number是強制解包之后的Int值
// number作用域僅限于這個大括號
} else {
print("字符串轉(zhuǎn)換整數(shù)失敗")
}
// 字符串轉(zhuǎn)換整數(shù)成功:123
4.等價寫法
var value = 100
if value <= 100, value >= 100, value == 100{
print(value)
}
if value <= 100 && value >= 100 && value == 100{
print(value)
}
5. 空合并運算符 ??
a ?? b
- a 是可選項
- b 是可選項 或者 不是可選項
- b 跟 a 的存儲類型必須相同
- 如果 a 不為nil噪伊,就返回 a
- 如果 a 為nil簿煌,就返回 b
- 如果 b 不是可選項氮唯,返回 a 時會自動解包
var num: Int?
num = 10
print(num ?? 20)
6 ??跟if let配合使用
let a: Int? = nil
let b: Int? = 2
if let c = a ?? b {
print(c) }
// 類似于if a != nil || b != nil
7. guard語句
當guard語句的條件為false時,就會執(zhí)行大括號里面的代碼
當guard語句的條件為true時姨伟,就會跳過guard語句
guard 條件 else {
// do something....
退出當前作用域
// return惩琉、break、continue夺荒、throw error
}
當使用guard語句進行可選項綁定時瞒渠,綁定的常量(let)、變量(var)也能在外層作用域中使用
func login(_ info: [String : String]) {
guard let username = info["username"] else {
print("請輸入用戶名")
return
}
guard let password = info["password"] else {
print("請輸入密碼")
return
}
// if username ....
// if password ....
print("用戶名:\(username)", "密碼:\(password)", "登陸ing")
}
8. 隱式解包(Implicitly Unwrapped Optional)
- 在某些情況下技扼,可選項一旦被設(shè)定值之后伍玖,就會一直擁有值
- 在這種情況下,可以去掉檢查淮摔,也不必每次訪問的時候都進行解包私沮,因為它能確定每次訪問的時候都有值
- 可以在類型后面加個感嘆號 ! 定義一個隱式解包的可選項
let num1: Int! = 10
let num2: Int = num1
if num1 != nil {
print(num1 + 6) // 16
}
if let num3 = num1 {
print(num3)
}