原文What Do @escaping and @noescape Mean In Swift 3
開(kāi)始用swift語(yǔ)言是很容易的越败,而且它確實(shí)是一門很吸引人的語(yǔ)言。但是隨著你頻繁的使用拆祈,你會(huì)逐漸接觸到swift更加復(fù)雜的結(jié)構(gòu).
在swift2中恨闪,你可能遇到過(guò)@noescape
屬性,你有沒(méi)有花一點(diǎn)時(shí)間去理解它的意思放坏?在swift3.0中咙咽,@noescape
已經(jīng)被移除了。為什么會(huì)這樣淤年?為什么swift3.0會(huì)引入@escaping
钧敞?回答這些問(wèn)題將成為這篇文章的主題蜡豹。
@noescape的含義
即使@noescape
在swift3.0中已經(jīng)被廢棄了,但是對(duì)于理解它的含義依然有用犁享。為什么呢?很簡(jiǎn)單豹休,在swift3.0中@noescape
被用作一個(gè)默認(rèn)值炊昆,讓我們開(kāi)始進(jìn)一步研究吧。
什么是逃逸閉包(Escaping Closure)
首先你需要理解什么是逃逸閉包威根。它的定義非常簡(jiǎn)單而且易于理解凤巨。如果一個(gè)閉包被作為一個(gè)參數(shù)傳遞給一個(gè)函數(shù),并且在函數(shù)return之后才被喚起執(zhí)行洛搀,那么這個(gè)閉包是逃逸閉包敢茁。并且這個(gè)閉包的參數(shù)是可以“逃出”這個(gè)函數(shù)體外的。理解了這個(gè)定義留美,這個(gè)逃逸閉包是不是就很好理解了呢彰檬?
If a closure is passed as an argument to a function and it is invoked after the function returns, the closure is escaping.
在swift2中,你可以標(biāo)記一個(gè)函數(shù)參數(shù)@noescape
屬性谎砾,來(lái)告訴編譯器傳遞給這個(gè)函數(shù)的閉包不允許“逃逸”出函數(shù)體外逢倍,讓我們看一下下面的例子,注意這是用swift2寫的景图。
import HealthKit
class HealthKitManager: NSObject {
private let healthStore = HKHealthStore()
func requestAuthorization(completion: (Bool, NSError?) -> Void) {
var shareTypes = Set<HKSampleType>()
var readTypes = Set<HKSampleType>()
// Add Workout Type
shareTypes.insert(HKSampleType.workoutType())
readTypes.insert(HKSampleType.workoutType())
// Request Authorization
healthStore.requestAuthorizationToShareTypes(shareTypes, readTypes: readTypes,completion: completion)
}
}
可以看到requestAuthorization(_:)
方法接收了一個(gè)參數(shù):一個(gè)閉包较雕。在swift2中,閉包默認(rèn)是可以“逃逸”到函數(shù)體外的挚币,這就是為什么上面的例子沒(méi)有報(bào)編譯錯(cuò)誤亮蒋。注意completion作為一個(gè)參數(shù)傳遞給HKHealthStore 的requestAuthorizationToShareTypes(_:readTypes:completion:)
方法,這個(gè)方法是異步的妆毕,也就是說(shuō)這個(gè)閉包會(huì)在requestAuthorization(_:)
函數(shù)return之后執(zhí)行慎玖。換句話說(shuō),我們傳給requestAuthorization(_:)
的這個(gè)閉包逃逸了笛粘,它已經(jīng)逃逸出了這個(gè)函數(shù)體凄吏。
如果我們給requestAuthorization(_:)
函數(shù)參數(shù)添加一個(gè)@noescape
屬性會(huì)變得非常有趣,下面是添加@noescape
屬性的例子
import HealthKit
class HealthKitManager: NSObject {
private let healthStore = HKHealthStore()
func requestAuthorization(@noescape completion: (Bool, NSError?) -> Void) {
var shareTypes = Set<HKSampleType>()
var readTypes = Set<HKSampleType>()
// Add Workout Type
shareTypes.insert(HKSampleType.workoutType())
readTypes.insert(HKSampleType.workoutType())
// Request Authorization
healthStore.requestAuthorizationToShareTypes(shareTypes, readTypes: readTypes,completion: completion)
}
}
我們直接告訴編譯器這個(gè)completion參數(shù)不能逃逸出函數(shù)體外闰蛔,結(jié)果就是編譯報(bào)錯(cuò)痕钢,并解釋了是什么錯(cuò)誤
swift3.0
下面給出上面例子的swift3版本。這個(gè)例子會(huì)展示“逃逸閉包”在swift3.0會(huì)有什么變化序六。
import HealthKit
class HealthKitManager: NSObject {
private let healthStore = HKHealthStore()
func requestAuthorization(@noescape completion: (Bool, Error?) -> Void) {
var shareTypes = Set<HKSampleType>()
var readTypes = Set<HKSampleType>()
// Add Workout Type
shareTypes.insert(HKSampleType.workoutType())
readTypes.insert(HKSampleType.workoutType())
// Request Authorization
healthStore.requestAuthorization(toShare: shareTypes, read: readTypes, completion: completion)
}
}
編譯器立即告訴我們@noescape
在swift3中是默認(rèn)的并且建議移除@noescape
任连。事實(shí)上,@noescape
在swift3中已經(jīng)被廢棄例诀,你以后都不會(huì)用到它了随抠。
因?yàn)槲覀儌鬟f給requestAuthorization(completion:)
(注意這個(gè)方法簽名在swift3變得不一樣了)函數(shù)的閉包逃逸了裁着,所以我們需要標(biāo)記這個(gè)參數(shù)escaping.我們使用一個(gè)新的屬性@escaping
。這是SE-0103的直接結(jié)果拱她,一個(gè)swift的進(jìn)化理論提出默認(rèn)創(chuàng)建不可逃逸的閉包二驰。這是一個(gè)非常受歡迎的改變?nèi)绻銌?wèn)我的話。
import HealthKit
class HealthKitManager: NSObject {
private let healthStore = HKHealthStore()
func requestAuthorization(completion: @escaping (Bool, Error?) -> Void) {
var shareTypes = Set<HKSampleType>()
var readTypes = Set<HKSampleType>()
// Add Workout Type
shareTypes.insert(HKSampleType.workoutType())
readTypes.insert(HKSampleType.workoutType())
// Request Authorization
healthStore.requestAuthorization(toShare: shareTypes, read: readTypes, completion: completion)
}
}
你可能注意到了秉沼,這個(gè)@escaping
屬性寫在參數(shù)類型的前面而不是參數(shù)名稱的前面桶雀。這是swift3里一個(gè)新的點(diǎn)。
@escaping的含義
這個(gè)提醒我們?nèi)ダ斫?code>@escaping屬性的含義唬复。因?yàn)樵趕wift3中閉包默認(rèn)是不可逃逸的矗积,逃逸閉包需要像這樣被標(biāo)記。@escaping
屬性讓我們可以那樣做敞咧。
我們通過(guò)@escaping
屬性標(biāo)記閉包棘捣,編譯錯(cuò)誤就消失了。
重點(diǎn)
這里幾點(diǎn)關(guān)于創(chuàng)建默認(rèn)不可逃逸閉包的好處: 最明顯的好處就是編譯器優(yōu)化你的代碼的性能和能力休建。如果編譯器知道這個(gè)閉包是不可逃逸的乍恐,它可以關(guān)注內(nèi)存管理的關(guān)鍵細(xì)節(jié)。
而且你可以在不可逃逸閉包里放心的使用self關(guān)鍵字测砂,因?yàn)檫@個(gè)閉包總是在函數(shù)return之前執(zhí)行禁熏,你不需要去使用一個(gè)弱引用去引用self.這對(duì)你而言是一個(gè)非常nice的功能。