1.在stack中被建立,運作於heap裡面
2.所有在closure裡面用到的物件會自動被保留
3.是一個物件
4.通常用來做callback函式
範例程式碼:
func test(data: Int, callback: @escaping (Bool) -> ()) {
callback(true)
}
test(data: 1) {
(result) -> Void in
if result {
print("success")
}
}
Note:
@escaping其實只有在程式執行會跳脫function的sequential的執行流程時才需要用到,例如:
EX1(非同步下載資料):
func test(callback: @escaping (Bool) -> ()) {
downloadData { //非同步,跳脫原本function的sequential執行流程
(result) -> Void in
callback(result)
}
}
EX2(非同步執行):
func test(callback: @escaping (Bool) -> ()) {
DispatchQueue.global.async { //非同步,跳脫原本function的sequential執行流程
(result) -> Void in
callback(result)
}
}
1. 全域函數(Global functions)有自己的名稱且不capture any values
2. 內嵌函數(Nested functions)有自己的名稱且能從自己被定義的function內capture any values
3. Closure表達式沒有自己的名字且從自己被定義的context範圍內capture values
Capture values指的是能夠存取及保留所用到的變數reference,即便那個變數定義在closure之外
以官方的例子來說明:
一個產生針對input加固定數字的function
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
makeIncrementer是一個裡面包含nested function(即incrementer)的function
以func incrementer()這個nested function為主來看,先記住runningTotal和amount都定義在func incrementer()以外
現在我們來產生一個固定針對input加10的function:let incrementByTen = makeIncrementer(forIncrement: 10)
incrementByTen這個function block內定義的變數沒有包含runningTotal和amount,仍就只有:
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
但是因為Closure能保留並存取用到的變數runningTotal和amount,所以incrementByTen這個function來對input加上10
如果我們現在創另外一個incrementer來對input加7:let incrementBySeven = makeIncrementer(forIncrement: 7)
incrementBySeven一樣能使用變數runningTotal和amount,且變數runningTotal和amount是新的一份,不會和incrementByTen共用變數runningTotal和amount,所以就能夠來進行加7,而不是加10。
1. 根據context推斷參數和回傳值型態
2. 從單一表達式中中暗含回傳值
3. 參數名稱簡化
4. closure為最後一個參數的簡化表達式
ex: A + B
用function:
funcAdd(a: Int,b: Int) -> Bool {
return a > b
}
assert(funcAdd(a:a,b:b), "error, a can not larger than b")
用closure:
assert(a > b, "error, a can not larger than b")
例如: 判斷數值是否為奇數
A)全部使用function:
funcTest1(arr: [Int]) -> Void {
for item in arr {
if funcIsOdd(val: item) {
print("\(item)")
}
}
}
funcTest2(arr: [Int]) -> Void {
for item in arr {
if funcIsEven(val: item) {
print("\(item)")
}
}
}
funcIsOdd(val: Int) -> Bool {
return val % 2 != 0
}
funcIsEven(val: Int) -> Bool {
return val % 2 == 0
}
let a = [1,2,3,4]
funcTest1(arr: a)
funcTest2(arr: a)
B) 使用Closure
funcTest(arr: [Int], closure: (val: Int) -> Bool) -> Void {
for item in arr {
if closure(val: item) {
print("\(item)")
}
}
}
let a = [1, 2, 3]
let isOdd = { $0%2 == 1} //note:isOdd就像是語言提供的基本語法,讓我們找出array中的數字是否為奇數
let isEven = { $0%2 == 0} //note:isEven就像是語言提供的基本語法,讓我們找出array中的數字是否偶數
funcTest(arr: a, isOdd)
funcTest(arr: a, isEven)
ref:
https://medium.com/%E5%BD%BC%E5%BE%97%E6%BD%98%E7%9A%84-swift-ios-app-%E9%96%8B%E7%99%BC%E6%95%99%E5%AE%A4/%E7%B0%A1%E6%98%93%E8%AA%AA%E6%98%8Eswift-4-closures-77351c3bf775
https://airspeedvelocity.net/2014/06/28/extending-the-swift-language-is-cool-but-be-careful/
官方說明:
https://docs.swift.org/swift-book/LanguageGuide/Closures.html