一個下午讓你掌握Swift基礎 ( 8/9 ) 閉包

簡介

這是一個 Swift 語言教程,基于最新的 iOS 9,Xcode 7.3 和 Swift 2.2,會為你介紹 Swift 編程非常基礎的內容。從電腦如何工作的全程基本原理到語言結構,你會足夠了解這門語言,來處理數據和管理代碼的行為。

快速鏈接


閉包

之前那篇文章全是關于函數的。但 Swift 有另一個對象,你可以使用它將代碼分解成可重復使用的塊,它叫做閉包(closure)

還記得你從簡單明了的值比如 10 和 “你好”開始,然后通過變量和常量給他們命名?閉包和函數很像 — 除了你在開始學習函數的時候,他們都有名字。

一個閉包是一個簡單的函數但沒有名字。你可以把他們賦值給變量然后傳遞他們,就像任意其他值一樣。聲明它們不需要創建一個完全形式的函數,這讓他們使用、傳遞都更方便,就像你在這篇文章里即將看到的。

閉包基礎

閉包叫這個名字是因為他們有能力把變量和常量“關閉”在閉包自己的 scope 里。這只是意味著,如果一個閉包想要從包圍的上下文中訪問、存儲和操作任意的變量或常量的值,它就可以。一個閉包體內的變量和常量被認為被閉包捕獲(capture)了。

你可能想問,“如果閉包是沒有名字的函數,那你怎么使用它們?”要使用一個閉包,你首先必須要把它賦值給一個變量或常量。在這種方式中,閉包是一個類型,如任何其他類型。

這是一個可以保存一個閉包的變量聲明:

var multiplyClosure: (Int, Int) -> Int

multiplyClosure 帶有兩個 Int 值并且返回一個 Int。注意這和為函數聲明一個變量完全相同。就像我說的,一個閉包是沒有名字的函數!

賦值一個閉包給一個變量,像這樣:

multiplyClosure = { (a: Int, b: Int) -> Int in
    return a * b
}

這看起來就像函數聲明,但有一點細微的變化。有相同的參數列表,->符號和返回類型。但對于閉包,這些出現在花括號里,并且有一個 in 關鍵字在返回類型的后面。

閉包變量定義了后,你可以使用它就像它是一個函數一樣,例如:

let result = multiplyClosure(4, 2)

就像你想的,result 等于 8。

簡寫語法

相比函數,閉包被設計的很輕量。有很多方式來縮短語法。首先,你可以去掉閉包里的 return,像這樣:

multiplyClosure = { (a: Int, b: Int) -> Int in
    a * b
}

如果閉包只有一行表達式,你可以移除返回語句,Swift 會為你推斷。

你還可以使用 Swift 的類型推斷來縮短語法,通過移除返回類型:

multiplyClosure = { (a: Int, b: Int) in
    a * b
}

這里唯一的區別是你移除掉了 -> Int。記住,你已經聲明 multiplyClosure 為一個返回一個 Int 的閉包了,所以你可以讓 Swift 為你推斷這部分的類型。

類型判斷允許我們更短一些。聲明閉包的時候參數的類型可以被移除,像這樣:

multiplyClosure = { (a, b) in
    a * b
}

最后,如果你想你甚至可以忽略參數列表。Swift 允許你通過數字指向每個參數,從零開始,像這樣:

multiplyClosure = {
    $0 * $1
}

參數列表,返回類型和 in 關鍵字全部沒了,你的新閉包聲明比原始版本遠遠要短了。像這樣的數字化參數真的應該只被用在短的、可愛的閉包里,就像這一個。如果太長了,要記憶每個數字化參數指向的是什么就會很混淆,那你就應該用命名語法。

現在讓我們看看這樣可以變得多么有用。

考慮如下代碼:

func operateOnNumbers(a: Int, _ b: Int,
                  operation: (Int, Int) -> Int) -> Int {
    let result = operation(a, b)
    print(result)
    return result
}

這聲明了一個叫做 operateOnNumbers 的函數,帶有前兩個 Int 值參數。第三個參數叫做 operation,顯示為一個函數類型。operateOnNumbers 自己返回一個 Int。

你然后可以結合一個閉包來使用 operateOnNumbers ,像這樣:

let addClosure = { (a: Int, b: Int) in
    a+b 
}
operateOnNumbers(4, 2, operation: addClosure)

記住,閉包只是沒有名字的函數。你也可以傳遞一個函數進去,作為 operateOnNumbers 的第三個參數,所以學習到這點的時候就不值得驚訝,像這樣:

func addFunction(a: Int, b: Int) -> Int {
    return a + b
}
operateOnNumbers(4, 2, operation: addFunction)

operateOnNumbers 被用相同的方式調用,不論 operation 是一個函數還是一個閉包。

但是,這里閉包語法的強力又再一次來到手邊了。你可以在調用 operateOnNumbers 函數的行內定義閉包,像這樣:

operateOnNumbers(4, 2, operation: { (a: Int, b: Int) -> Int in
    return a + b
})

不需要定義閉包,然后把它賦值給一個本地變量或常量;你可以簡單地聲明閉包,就在把它作為一個參數傳遞給函數的地方!

但回顧一下你可以簡化語法來移除很多樣板代碼。因此你可以把上面的減少成下面這樣:

operateOnNumbers(4, 2, operation: {
    $0 + $1
})

還有一種方式可以簡化語法,但只有在閉包是傳遞給函數的最后一個參數的時候才可以。這個例子里,你可以把閉包移到函數調用外面:

operateOnNumbers(4, 2) {
    $0 + $1
}

這看起來可能很奇怪,但這就和前一個代碼段相同,除了你移除掉了 operation 標簽,并且把大括號拉到了函數調用參數列表的外面。這叫做尾隨閉包語法(trailing closure syntax)

沒有返回值的閉包

直到現在,你看到的閉包都帶有一個或更多參數,并且有返回值。但就像函數,閉包沒有被要求一定要做這些事情。這是你如何聲明一個閉包,不帶有參數并且不返回東西:

let voidClosure: () -> Void = {
    print("幾個高級和弦 幾個切分音 我寫的 Swift 代碼也可以很有音樂性!")}
voidClosure()

這個閉包的類型是 () -> Void。空的括號表示沒有參數。你必須聲明一個返回類型,所以 Swift 才知道你在聲明一個閉包。這是 Void 出現在手邊的地方,它就是它的名字表示的意思:閉包什么都不返回。

從圍繞 scope 中捕獲

最終,讓我們回到定義一個閉包的特征:它可以訪問自己的 scope 里的變量和常量。

**注意:**回顧一下,scope 定義了在哪里一個實體(變量,常量,等等)是可以訪問的。介紹 if 語句的時候你看到了一個新的 scope。閉包也介紹了一個新的 scope,并且繼承了所有在它定義的地方可見的實體到 scope 里。

例如,采用下面的閉包:

var counter = 0
let incrementCounter = {
    counter += 1
}

incrementCounter 非常簡單:它遞增了 counter 變量。counter 變量定義在閉包外面。閉包能夠訪問變量的原因是它被定義在和變量相同的 scope 里。閉包被稱作捕獲(capture)了 counter 變量。它對于變量做的任何改變在閉包里和外面都是可見的。

假設你調用了5次閉包,像這樣:

incrementCounter()
incrementCounter()
incrementCounter()
incrementCounter()
incrementCounter()

這5次調用之后,counter 會等于5。

閉包可以被用來從圍繞的 scope 捕獲變量的事實會變得非常有用。例如,你可以寫這個函數:

func countingClosure() -> (() -> Int) {
    var counter = 0
    let incrementCounter: () -> Int = {
        counter += 1
        return counter
    }
    return incrementCounter
}

函數不帶有參數,返回一個閉包。它返回的閉包自己不帶有參數并且返回一個 Int。

這個函數每次被調用的時候會返回的閉包會遞增它的內置 counter(計數器)。每次你調用這個函數會獲得一個不同的 counter。

例如,這個可以被這樣使用:

let counter1 = countingClosure()
let counter2 = countingClosure()
counter1() // 0
counter2() // 0
counter1() // 1
counter1() // 2
counter2() // 1

函數創建的兩個 counter 互相獨立,單獨技術。干凈!

這就是閉包啦!

關鍵點

  • 閉包(closures)是沒有名字的函數。他們可以被賦值給變量并且作為參數傳遞給函數。
  • 閉包有簡寫語法(shorthand syntax),讓他們比使用其他函數簡單許多。
  • 一個閉包可以從它的包圍上下文中捕獲(capture)變量和常量。

接下來去哪兒?

閉包和函數是把你的代碼存儲為可復用代碼塊的基本類型。除了聲明和調用他們,你也看到了它們在作為參數傳遞給其它函數和閉包的時候有多么方便。

你會在接下來的文章里返回處理數據,你會學習可選值和集合類型。還有,保持閉包和函數在腦海里,你會看到如何把每個東西組合—變量、常量和函數—在一起,用到你自己定義的類型里,從后面的“結構體”文章開始。

在此期間,看看下面的挑戰來測試你的知識,然后再繼續。

挑戰

挑戰 A:重復你自己

你的第一個挑戰是寫一個函數,會運行一個給定的閉包指定次數。

像這樣聲明函數:

func repeatTask(times: Int, task: () -> Void)

函數應該運行 task 閉包,times 次。
使用這個函數 print “一個下午掌握 Swift 基礎!” 10 次。

挑戰 B:閉包總和

這個挑戰里,你要寫一個函數,你可以復用來創建不同的數學和。

像這樣聲明函數:

func mathSum(times: Int, operation: (Int) -> Int) -> Int

第一個參數,times,定義了重復的次數。第二個參數,operation,是一個閉包,帶有一個 Int 并且返回一個 Int。

operation 應該有一個參數是當前重復的次數,并且以此次重復結果的數字作為返回值。

mathSum 應該重復 times 次,從 1 開始。它應該保持每次重復的 operation 結果的和。

使用函數找到前 10 個平方數的和,等于 385。然后用函數找到前 10 個斐波那契數列數字的和,等于 143。對于斐波那契數列,你可以之前在前一章挑戰里寫的函數—或者找一個現成的如果你沒有做的話!

挑戰源代碼

https://yunpan.cn/cBWszdN7nkNjr (提取碼:aaa4)


介紹

歡迎來到Swift世界!Swift是一門蘋果在2014年夏天發布的編程語言。從那之后,Swift發布了一個主要的版本跳躍,成為了開始在蘋果平臺:iOS,OS X,watchOS和tvOS開發的最簡單的方式。

誰適合這篇教程

這篇教程適合懂一點編程、并且希望學習Swift的人。也許你已經為網站寫過一些JavaScript代碼,或者用Python寫過一些簡短的程序。這篇教程就是為你準備的!你會學習到編程的基本概念,同時也會成為Swift語言小能手。

如果你是赤裸裸的編程新手,這篇教程也是為你準備的!教程里貫穿有簡短的鍛煉和挑戰來給你一些編程練習,同時測試你的知識。

需要準備什么

要看這篇教程,你需要準備如下的東西:

  • 一臺運行OS X El Captian(10.11)的Mac,帶有最新發布的更新并且安裝了安全補丁。這樣你才能夠安裝需要的開發工具:最新版本的Xcode。
  • Xcode 7.3 或更新的版本。Xcode是用Swift寫代碼的主要開發工具。最小也需要Xcode 7.3版本,因為那個版本包含Swift 2.2。你可以免費從Mac App Store下載Xcode的最新版本,這里:http://apple.co/1FLn51R

如果你還沒有安裝Xcode最新版本,在繼續看下面的教程前要確定安裝。

如何使用這篇教程

每篇教程都會介紹觸手可及的話題理論,伴隨大量Swift代碼來示范在學習的實際的應用程序。

教程里的所有代碼都是平臺中立的;這意味著不是為iOS、OS X或任何其它平臺而特定。代碼在playgrounds里運行,你在本篇中已經學習了。

在剩下的教程里,你可以把代碼在自己的playground里輸入進去。這樣你就可以和代碼“玩耍”(play around),做一些改變立即就能看見代碼運行的結果。

剩下的教程里會貫穿實際小練習,都是簡短的練習,關于觸手可及的主題。每篇的末尾也有挑戰,會有編程問題也會有長一點的代碼練習來測試你的知識。做完就能掌握大部分的Swift基礎知識。

教程更新

教程會隨Swift語言的更新而更新,會發布在我的簡書和知乎專欄上,搜索“張嘉夫”即可關注我。

目錄

本教程從一些基礎工作開始來讓你起步:

  • 第1篇,編程本質 & Playground基礎 - 這就是,到編程世界的入門介紹!你會從電腦和編程的預覽開始,然后剩余時間給Swift playground打個招呼。
  • 第2篇,變量 & 常量 - 你會學習到變量和常量,這是用來存儲數據的“地方”。你也會學習數據類型以及Swift如何追蹤數據類型并在代碼中進行傳輸。
  • 第3篇,數字類型 & 操作 - 你會從基礎的數字類型比如整形和浮點型數字開始,當然也包括布爾類型。也會看到一些在實際操作,從比較到算數操作如加減。
  • 第4篇,字符串 - 用字符串來存儲文字-從按鈕里的文字到圖片的標注到這篇教程全部的文字,存儲這所有的一切!你會學習到string和character類型,以及基于這些類型的一些常見操作。

在腦海中有基礎數據類型后,就該用那些數據做一些事情了:

  • 第5篇,做判斷 - 代碼不總是直接從頭運行到尾。你會學習在代碼里如何做判決并且設定情況來運行某段代碼。
  • 第6篇,重復步驟 - 繼續不要讓代碼直線運行的主題,你會學習到如何使用循環來重復某些步驟。
  • 第7篇,函數 - 函數是Swift中用來構建代碼的基礎建筑。你會學習到如何定義函數來分組代碼到可復用單元中。
  • 第8篇,閉包(Closures) - 閉包和函數很接近。你會學習到如何使用它們來輕松傳遞代碼塊。

最后一篇會回到數據:

  • 第9節,可選值 - 這篇講可選值,Swift中的一種特殊類型,表示既有可能是一個真實的值也有可能沒有值。這篇的最后你會知道為什么要用可選值以及如何安全地使用它們。

這些基礎會讓你快速開始Swift之路,做好接觸更高級編程主題的準備。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容