簡(jiǎn)介
這是一個(gè) Swift 語(yǔ)言教程,基于最新的 iOS 9,Xcode 7.3 和 Swift 2.2,會(huì)為你介紹 Swift 編程非常基礎(chǔ)的內(nèi)容。從電腦如何工作的全程基本原理到語(yǔ)言結(jié)構(gòu),你會(huì)足夠了解這門(mén)語(yǔ)言,來(lái)處理數(shù)據(jù)和管理代碼的行為。
快速鏈接
- 一個(gè)下午讓你掌握Swift基礎(chǔ) ( 1/9 ) 編程本質(zhì) & Playground基礎(chǔ)
- 一個(gè)下午讓你掌握Swift基礎(chǔ) ( 2/9 ) 變量 & 常量
- 一個(gè)下午讓你掌握Swift基礎(chǔ) ( 3/9 ) 數(shù)字類(lèi)型 & 操作
- 一個(gè)下午讓你掌握Swift基礎(chǔ) ( 4/9 ) 字符串
- 一個(gè)下午讓你掌握Swift基礎(chǔ) ( 5/9 ) 做判斷
- 一個(gè)下午讓你掌握Swift基礎(chǔ) ( 6/9 ) 重復(fù)步驟
- 一個(gè)下午讓你掌握Swift基礎(chǔ) ( 7/9 ) 函數(shù)
- 一個(gè)下午讓你掌握Swift基礎(chǔ) ( 8/9 ) 閉包
- 一個(gè)下午讓你掌握Swift基礎(chǔ) ( 9/9 ) 可選值
- Swift 初學(xué)者 ( 10/12 ) 數(shù)組
- Swift 初學(xué)者 ( 11/12 ) 字典
- Swift 初學(xué)者 ( 12/12 ) 集合
閉包
之前那篇文章全是關(guān)于函數(shù)的。但 Swift 有另一個(gè)對(duì)象,你可以使用它將代碼分解成可重復(fù)使用的塊,它叫做閉包(closure)。
還記得你從簡(jiǎn)單明了的值比如 10 和 “你好”開(kāi)始,然后通過(guò)變量和常量給他們命名?閉包和函數(shù)很像 — 除了你在開(kāi)始學(xué)習(xí)函數(shù)的時(shí)候,他們都有名字。
一個(gè)閉包是一個(gè)簡(jiǎn)單的函數(shù)但沒(méi)有名字。你可以把他們賦值給變量然后傳遞他們,就像任意其他值一樣。聲明它們不需要?jiǎng)?chuàng)建一個(gè)完全形式的函數(shù),這讓他們使用、傳遞都更方便,就像你在這篇文章里即將看到的。
閉包基礎(chǔ)
閉包叫這個(gè)名字是因?yàn)樗麄冇心芰Π炎兞亢统A俊瓣P(guān)閉”在閉包自己的 scope 里。這只是意味著,如果一個(gè)閉包想要從包圍的上下文中訪(fǎng)問(wèn)、存儲(chǔ)和操作任意的變量或常量的值,它就可以。一個(gè)閉包體內(nèi)的變量和常量被認(rèn)為被閉包捕獲(capture)了。
你可能想問(wèn),“如果閉包是沒(méi)有名字的函數(shù),那你怎么使用它們?”要使用一個(gè)閉包,你首先必須要把它賦值給一個(gè)變量或常量。在這種方式中,閉包是一個(gè)類(lèi)型,如任何其他類(lèi)型。
這是一個(gè)可以保存一個(gè)閉包的變量聲明:
var multiplyClosure: (Int, Int) -> Int
multiplyClosure 帶有兩個(gè) Int 值并且返回一個(gè) Int。注意這和為函數(shù)聲明一個(gè)變量完全相同。就像我說(shuō)的,一個(gè)閉包是沒(méi)有名字的函數(shù)!
賦值一個(gè)閉包給一個(gè)變量,像這樣:
multiplyClosure = { (a: Int, b: Int) -> Int in
return a * b
}
這看起來(lái)就像函數(shù)聲明,但有一點(diǎn)細(xì)微的變化。有相同的參數(shù)列表,->符號(hào)和返回類(lèi)型。但對(duì)于閉包,這些出現(xiàn)在花括號(hào)里,并且有一個(gè) in 關(guān)鍵字在返回類(lèi)型的后面。
閉包變量定義了后,你可以使用它就像它是一個(gè)函數(shù)一樣,例如:
let result = multiplyClosure(4, 2)
就像你想的,result 等于 8。
簡(jiǎn)寫(xiě)語(yǔ)法
相比函數(shù),閉包被設(shè)計(jì)的很輕量。有很多方式來(lái)縮短語(yǔ)法。首先,你可以去掉閉包里的 return,像這樣:
multiplyClosure = { (a: Int, b: Int) -> Int in
a * b
}
如果閉包只有一行表達(dá)式,你可以移除返回語(yǔ)句,Swift 會(huì)為你推斷。
你還可以使用 Swift 的類(lèi)型推斷來(lái)縮短語(yǔ)法,通過(guò)移除返回類(lèi)型:
multiplyClosure = { (a: Int, b: Int) in
a * b
}
這里唯一的區(qū)別是你移除掉了 -> Int。記住,你已經(jīng)聲明 multiplyClosure 為一個(gè)返回一個(gè) Int 的閉包了,所以你可以讓 Swift 為你推斷這部分的類(lèi)型。
類(lèi)型判斷允許我們更短一些。聲明閉包的時(shí)候參數(shù)的類(lèi)型可以被移除,像這樣:
multiplyClosure = { (a, b) in
a * b
}
最后,如果你想你甚至可以忽略參數(shù)列表。Swift 允許你通過(guò)數(shù)字指向每個(gè)參數(shù),從零開(kāi)始,像這樣:
multiplyClosure = {
$0 * $1
}
參數(shù)列表,返回類(lèi)型和 in 關(guān)鍵字全部沒(méi)了,你的新閉包聲明比原始版本遠(yuǎn)遠(yuǎn)要短了。像這樣的數(shù)字化參數(shù)真的應(yīng)該只被用在短的、可愛(ài)的閉包里,就像這一個(gè)。如果太長(zhǎng)了,要記憶每個(gè)數(shù)字化參數(shù)指向的是什么就會(huì)很混淆,那你就應(yīng)該用命名語(yǔ)法。
現(xiàn)在讓我們看看這樣可以變得多么有用。
考慮如下代碼:
func operateOnNumbers(a: Int, _ b: Int,
operation: (Int, Int) -> Int) -> Int {
let result = operation(a, b)
print(result)
return result
}
這聲明了一個(gè)叫做 operateOnNumbers 的函數(shù),帶有前兩個(gè) Int 值參數(shù)。第三個(gè)參數(shù)叫做 operation,顯示為一個(gè)函數(shù)類(lèi)型。operateOnNumbers 自己返回一個(gè) Int。
你然后可以結(jié)合一個(gè)閉包來(lái)使用 operateOnNumbers ,像這樣:
let addClosure = { (a: Int, b: Int) in
a+b
}
operateOnNumbers(4, 2, operation: addClosure)
記住,閉包只是沒(méi)有名字的函數(shù)。你也可以傳遞一個(gè)函數(shù)進(jìn)去,作為 operateOnNumbers 的第三個(gè)參數(shù),所以學(xué)習(xí)到這點(diǎn)的時(shí)候就不值得驚訝,像這樣:
func addFunction(a: Int, b: Int) -> Int {
return a + b
}
operateOnNumbers(4, 2, operation: addFunction)
operateOnNumbers 被用相同的方式調(diào)用,不論 operation 是一個(gè)函數(shù)還是一個(gè)閉包。
但是,這里閉包語(yǔ)法的強(qiáng)力又再一次來(lái)到手邊了。你可以在調(diào)用 operateOnNumbers 函數(shù)的行內(nèi)定義閉包,像這樣:
operateOnNumbers(4, 2, operation: { (a: Int, b: Int) -> Int in
return a + b
})
不需要定義閉包,然后把它賦值給一個(gè)本地變量或常量;你可以簡(jiǎn)單地聲明閉包,就在把它作為一個(gè)參數(shù)傳遞給函數(shù)的地方!
但回顧一下你可以簡(jiǎn)化語(yǔ)法來(lái)移除很多樣板代碼。因此你可以把上面的減少成下面這樣:
operateOnNumbers(4, 2, operation: {
$0 + $1
})
還有一種方式可以簡(jiǎn)化語(yǔ)法,但只有在閉包是傳遞給函數(shù)的最后一個(gè)參數(shù)的時(shí)候才可以。這個(gè)例子里,你可以把閉包移到函數(shù)調(diào)用外面:
operateOnNumbers(4, 2) {
$0 + $1
}
這看起來(lái)可能很奇怪,但這就和前一個(gè)代碼段相同,除了你移除掉了 operation 標(biāo)簽,并且把大括號(hào)拉到了函數(shù)調(diào)用參數(shù)列表的外面。這叫做尾隨閉包語(yǔ)法(trailing closure syntax)。
沒(méi)有返回值的閉包
直到現(xiàn)在,你看到的閉包都帶有一個(gè)或更多參數(shù),并且有返回值。但就像函數(shù),閉包沒(méi)有被要求一定要做這些事情。這是你如何聲明一個(gè)閉包,不帶有參數(shù)并且不返回東西:
let voidClosure: () -> Void = {
print("幾個(gè)高級(jí)和弦 幾個(gè)切分音 我寫(xiě)的 Swift 代碼也可以很有音樂(lè)性!")}
voidClosure()
這個(gè)閉包的類(lèi)型是 () -> Void。空的括號(hào)表示沒(méi)有參數(shù)。你必須聲明一個(gè)返回類(lèi)型,所以 Swift 才知道你在聲明一個(gè)閉包。這是 Void 出現(xiàn)在手邊的地方,它就是它的名字表示的意思:閉包什么都不返回。
從圍繞 scope 中捕獲
最終,讓我們回到定義一個(gè)閉包的特征:它可以訪(fǎng)問(wèn)自己的 scope 里的變量和常量。
**注意:**回顧一下,scope 定義了在哪里一個(gè)實(shí)體(變量,常量,等等)是可以訪(fǎng)問(wèn)的。介紹 if 語(yǔ)句的時(shí)候你看到了一個(gè)新的 scope。閉包也介紹了一個(gè)新的 scope,并且繼承了所有在它定義的地方可見(jiàn)的實(shí)體到 scope 里。
例如,采用下面的閉包:
var counter = 0
let incrementCounter = {
counter += 1
}
incrementCounter 非常簡(jiǎn)單:它遞增了 counter 變量。counter 變量定義在閉包外面。閉包能夠訪(fǎng)問(wèn)變量的原因是它被定義在和變量相同的 scope 里。閉包被稱(chēng)作捕獲(capture)了 counter 變量。它對(duì)于變量做的任何改變?cè)陂]包里和外面都是可見(jiàn)的。
假設(shè)你調(diào)用了5次閉包,像這樣:
incrementCounter()
incrementCounter()
incrementCounter()
incrementCounter()
incrementCounter()
這5次調(diào)用之后,counter 會(huì)等于5。
閉包可以被用來(lái)從圍繞的 scope 捕獲變量的事實(shí)會(huì)變得非常有用。例如,你可以寫(xiě)這個(gè)函數(shù):
func countingClosure() -> (() -> Int) {
var counter = 0
let incrementCounter: () -> Int = {
counter += 1
return counter
}
return incrementCounter
}
函數(shù)不帶有參數(shù),返回一個(gè)閉包。它返回的閉包自己不帶有參數(shù)并且返回一個(gè) Int。
這個(gè)函數(shù)每次被調(diào)用的時(shí)候會(huì)返回的閉包會(huì)遞增它的內(nèi)置 counter(計(jì)數(shù)器)。每次你調(diào)用這個(gè)函數(shù)會(huì)獲得一個(gè)不同的 counter。
例如,這個(gè)可以被這樣使用:
let counter1 = countingClosure()
let counter2 = countingClosure()
counter1() // 0
counter2() // 0
counter1() // 1
counter1() // 2
counter2() // 1
函數(shù)創(chuàng)建的兩個(gè) counter 互相獨(dú)立,單獨(dú)技術(shù)。干凈!
這就是閉包啦!
關(guān)鍵點(diǎn)
- 閉包(closures)是沒(méi)有名字的函數(shù)。他們可以被賦值給變量并且作為參數(shù)傳遞給函數(shù)。
- 閉包有簡(jiǎn)寫(xiě)語(yǔ)法(shorthand syntax),讓他們比使用其他函數(shù)簡(jiǎn)單許多。
- 一個(gè)閉包可以從它的包圍上下文中捕獲(capture)變量和常量。
接下來(lái)去哪兒?
閉包和函數(shù)是把你的代碼存儲(chǔ)為可復(fù)用代碼塊的基本類(lèi)型。除了聲明和調(diào)用他們,你也看到了它們?cè)谧鳛閰?shù)傳遞給其它函數(shù)和閉包的時(shí)候有多么方便。
你會(huì)在接下來(lái)的文章里返回處理數(shù)據(jù),你會(huì)學(xué)習(xí)可選值和集合類(lèi)型。還有,保持閉包和函數(shù)在腦海里,你會(huì)看到如何把每個(gè)東西組合—變量、常量和函數(shù)—在一起,用到你自己定義的類(lèi)型里,從后面的“結(jié)構(gòu)體”文章開(kāi)始。
在此期間,看看下面的挑戰(zhàn)來(lái)測(cè)試你的知識(shí),然后再繼續(xù)。
挑戰(zhàn)
挑戰(zhàn) A:重復(fù)你自己
你的第一個(gè)挑戰(zhàn)是寫(xiě)一個(gè)函數(shù),會(huì)運(yùn)行一個(gè)給定的閉包指定次數(shù)。
像這樣聲明函數(shù):
func repeatTask(times: Int, task: () -> Void)
函數(shù)應(yīng)該運(yùn)行 task 閉包,times 次。
使用這個(gè)函數(shù) print “一個(gè)下午掌握 Swift 基礎(chǔ)!” 10 次。
挑戰(zhàn) B:閉包總和
這個(gè)挑戰(zhàn)里,你要寫(xiě)一個(gè)函數(shù),你可以復(fù)用來(lái)創(chuàng)建不同的數(shù)學(xué)和。
像這樣聲明函數(shù):
func mathSum(times: Int, operation: (Int) -> Int) -> Int
第一個(gè)參數(shù),times,定義了重復(fù)的次數(shù)。第二個(gè)參數(shù),operation,是一個(gè)閉包,帶有一個(gè) Int 并且返回一個(gè) Int。
operation 應(yīng)該有一個(gè)參數(shù)是當(dāng)前重復(fù)的次數(shù),并且以此次重復(fù)結(jié)果的數(shù)字作為返回值。
mathSum 應(yīng)該重復(fù) times 次,從 1 開(kāi)始。它應(yīng)該保持每次重復(fù)的 operation 結(jié)果的和。
使用函數(shù)找到前 10 個(gè)平方數(shù)的和,等于 385。然后用函數(shù)找到前 10 個(gè)斐波那契數(shù)列數(shù)字的和,等于 143。對(duì)于斐波那契數(shù)列,你可以之前在前一章挑戰(zhàn)里寫(xiě)的函數(shù)—或者找一個(gè)現(xiàn)成的如果你沒(méi)有做的話(huà)!
挑戰(zhàn)源代碼
https://yunpan.cn/cBWszdN7nkNjr (提取碼:aaa4)
介紹
歡迎來(lái)到Swift世界!Swift是一門(mén)蘋(píng)果在2014年夏天發(fā)布的編程語(yǔ)言。從那之后,Swift發(fā)布了一個(gè)主要的版本跳躍,成為了開(kāi)始在蘋(píng)果平臺(tái):iOS,OS X,watchOS和tvOS開(kāi)發(fā)的最簡(jiǎn)單的方式。
誰(shuí)適合這篇教程
這篇教程適合懂一點(diǎn)編程、并且希望學(xué)習(xí)Swift的人。也許你已經(jīng)為網(wǎng)站寫(xiě)過(guò)一些JavaScript代碼,或者用Python寫(xiě)過(guò)一些簡(jiǎn)短的程序。這篇教程就是為你準(zhǔn)備的!你會(huì)學(xué)習(xí)到編程的基本概念,同時(shí)也會(huì)成為Swift語(yǔ)言小能手。
如果你是赤裸裸的編程新手,這篇教程也是為你準(zhǔn)備的!教程里貫穿有簡(jiǎn)短的鍛煉和挑戰(zhàn)來(lái)給你一些編程練習(xí),同時(shí)測(cè)試你的知識(shí)。
需要準(zhǔn)備什么
要看這篇教程,你需要準(zhǔn)備如下的東西:
- 一臺(tái)運(yùn)行OS X El Captian(10.11)的Mac,帶有最新發(fā)布的更新并且安裝了安全補(bǔ)丁。這樣你才能夠安裝需要的開(kāi)發(fā)工具:最新版本的Xcode。
- Xcode 7.3 或更新的版本。Xcode是用Swift寫(xiě)代碼的主要開(kāi)發(fā)工具。最小也需要Xcode 7.3版本,因?yàn)槟莻€(gè)版本包含Swift 2.2。你可以免費(fèi)從Mac App Store下載Xcode的最新版本,這里:http://apple.co/1FLn51R。
如果你還沒(méi)有安裝Xcode最新版本,在繼續(xù)看下面的教程前要確定安裝。
如何使用這篇教程
每篇教程都會(huì)介紹觸手可及的話(huà)題理論,伴隨大量Swift代碼來(lái)示范在學(xué)習(xí)的實(shí)際的應(yīng)用程序。
教程里的所有代碼都是平臺(tái)中立的;這意味著不是為iOS、OS X或任何其它平臺(tái)而特定。代碼在playgrounds里運(yùn)行,你在本篇中已經(jīng)學(xué)習(xí)了。
在剩下的教程里,你可以把代碼在自己的playground里輸入進(jìn)去。這樣你就可以和代碼“玩耍”(play around),做一些改變立即就能看見(jiàn)代碼運(yùn)行的結(jié)果。
剩下的教程里會(huì)貫穿實(shí)際小練習(xí),都是簡(jiǎn)短的練習(xí),關(guān)于觸手可及的主題。每篇的末尾也有挑戰(zhàn),會(huì)有編程問(wèn)題也會(huì)有長(zhǎng)一點(diǎn)的代碼練習(xí)來(lái)測(cè)試你的知識(shí)。做完就能掌握大部分的Swift基礎(chǔ)知識(shí)。
教程更新
教程會(huì)隨Swift語(yǔ)言的更新而更新,會(huì)發(fā)布在我的簡(jiǎn)書(shū)和知乎專(zhuān)欄上,搜索“張嘉夫”即可關(guān)注我。
目錄
本教程從一些基礎(chǔ)工作開(kāi)始來(lái)讓你起步:
- 第1篇,編程本質(zhì) & Playground基礎(chǔ) - 這就是,到編程世界的入門(mén)介紹!你會(huì)從電腦和編程的預(yù)覽開(kāi)始,然后剩余時(shí)間給Swift playground打個(gè)招呼。
- 第2篇,變量 & 常量 - 你會(huì)學(xué)習(xí)到變量和常量,這是用來(lái)存儲(chǔ)數(shù)據(jù)的“地方”。你也會(huì)學(xué)習(xí)數(shù)據(jù)類(lèi)型以及Swift如何追蹤數(shù)據(jù)類(lèi)型并在代碼中進(jìn)行傳輸。
- 第3篇,數(shù)字類(lèi)型 & 操作 - 你會(huì)從基礎(chǔ)的數(shù)字類(lèi)型比如整形和浮點(diǎn)型數(shù)字開(kāi)始,當(dāng)然也包括布爾類(lèi)型。也會(huì)看到一些在實(shí)際操作,從比較到算數(shù)操作如加減。
- 第4篇,字符串 - 用字符串來(lái)存儲(chǔ)文字-從按鈕里的文字到圖片的標(biāo)注到這篇教程全部的文字,存儲(chǔ)這所有的一切!你會(huì)學(xué)習(xí)到string和character類(lèi)型,以及基于這些類(lèi)型的一些常見(jiàn)操作。
在腦海中有基礎(chǔ)數(shù)據(jù)類(lèi)型后,就該用那些數(shù)據(jù)做一些事情了:
- 第5篇,做判斷 - 代碼不總是直接從頭運(yùn)行到尾。你會(huì)學(xué)習(xí)在代碼里如何做判決并且設(shè)定情況來(lái)運(yùn)行某段代碼。
- 第6篇,重復(fù)步驟 - 繼續(xù)不要讓代碼直線(xiàn)運(yùn)行的主題,你會(huì)學(xué)習(xí)到如何使用循環(huán)來(lái)重復(fù)某些步驟。
- 第7篇,函數(shù) - 函數(shù)是Swift中用來(lái)構(gòu)建代碼的基礎(chǔ)建筑。你會(huì)學(xué)習(xí)到如何定義函數(shù)來(lái)分組代碼到可復(fù)用單元中。
- 第8篇,閉包(Closures) - 閉包和函數(shù)很接近。你會(huì)學(xué)習(xí)到如何使用它們來(lái)輕松傳遞代碼塊。
最后一篇會(huì)回到數(shù)據(jù):
- 第9節(jié),可選值 - 這篇講可選值,Swift中的一種特殊類(lèi)型,表示既有可能是一個(gè)真實(shí)的值也有可能沒(méi)有值。這篇的最后你會(huì)知道為什么要用可選值以及如何安全地使用它們。
這些基礎(chǔ)會(huì)讓你快速開(kāi)始Swift之路,做好接觸更高級(jí)編程主題的準(zhǔn)備。