閉包引入
計算1個數(shù)的平方
- 函數(shù)寫法
func square(param:Int) -> Int{
return param * param
}
square(param:3)
- 閉包寫法
let squareCloure = { (param:Int) -> Int in
return param * param
}
squareCloure(3)
閉包含義
- 閉包是可以被傳遞和引用的一個獨立模塊
- 閉包能夠捕獲和存儲定義在其上下文中的任何常量和變量,即
閉合并包裹那些常量和變量,因此被稱為“閉包” - 閉包符合如下三種形式中的一種:
- 全局函數(shù)是一個有名字但不會捕獲任何值的閉包
- 內(nèi)嵌函數(shù)是一個有名字且能從其上層函數(shù)捕獲值的閉包(函數(shù)中的嵌套函數(shù)知識點)
- 閉包表達(dá)式是一個輕量級語法,可以捕獲其上下文中常量或變量值的沒有名字的閉包
- 閉包和函數(shù)一樣也是引用類型
簡單案例
- 案例一
let demo= { print("Swift 閉包實例。") }
demo()
- 案例二
let divide = {(val1: Int, val2: Int) -> Int in
return val1 / val2
}
let result = divide(200, 20)
print (result)
閉包表達(dá)式
閉包表達(dá)式語法有如下的一般形式:
{ (parameters) -> (return type) in
statements
}
- 閉包表達(dá)式由一對
{}開始與結(jié)束 - 由
in關(guān)鍵字將閉包分割成兩部分:參數(shù)與返回值、閉包體 - 閉包參數(shù)與函數(shù)參數(shù)的區(qū)別
- 形式參數(shù)不能提供默認(rèn)值,其他和函數(shù)一樣
閉包主要知識點
參數(shù)名稱縮寫
- Swift 提供了參數(shù)名稱的縮寫功能,直接通過
$0,$1,$2來順序調(diào)用閉包的參數(shù) - 在閉包表達(dá)式中使用參數(shù)名稱縮寫,可以在閉包參數(shù)列表中省略對其定義
- 參數(shù)類型可以通過函數(shù)類型進(jìn)行推斷
- 在單行閉包的時候,return 關(guān)鍵字可以省略
- 參數(shù)名稱省略以后,in 關(guān)鍵字也可以被省略
//從數(shù)組中篩選指出合適的數(shù)據(jù)組成新的數(shù)組
func getList(score:[Int], con:(Int)->Bool) -> [Int]{
var newScore:[Int] = [Int]()
for item in score {
if con(item) {
newScore.append(item)
}
}
return newScore
}
let newAarray = getList(score: [75,60,95,45,85], con:{(s:Int)->Bool in return s>80})
print(newAarray)
第一種簡寫: 省略 ->與返回類型(根據(jù)后面表達(dá)式可以推斷返回值是一個Bool)
let newAarray = getList(score: [75,60,95,45,85], con:{(s:Int) in return s>80})
第二種簡寫:省略參數(shù)類型和括號(根據(jù)函數(shù)的參數(shù)可推斷傳進(jìn)來的必然是Int)
let newAarray = getList(score: [75,60,95,45,85], con:{s in return s>80})
第三種簡寫:省略return關(guān)鍵字
let newAarray = getList(score: [75,60,95,45,85], con:{s in s>80})
第四種簡寫:參數(shù)名稱縮寫,省略參數(shù)聲明和in,改為$0
let newAarray = getList(score: [75,60,95,45,85], con:{$0>80})
捕獲
- 閉包可以從上下文環(huán)境中捕獲常量、變量,并在自己的作用域內(nèi)使用
- Swift最簡單的閉包形式是嵌套函數(shù),也就是定義在其他函數(shù)的函數(shù)體內(nèi)的函數(shù),嵌套函數(shù)可以捕獲其外部函數(shù)所有的參數(shù)以及定義的常量和變量。
func makeIncrementor(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementor() -> Int {
runningTotal += amount
return runningTotal
}
return incrementor
}
let incrementByTen = makeIncrementor(forIncrement: 10)
// 返回的值為10
print(incrementByTen())
// 返回的值為20
print(incrementByTen())
// 返回的值為30
print(incrementByTen())
尾隨閉包
- 尾隨閉包是一個書寫在函數(shù)括號之后的閉包表達(dá)式,函數(shù)支持將其作為最后一個參數(shù)調(diào)用
- 閉包是函數(shù)的最后一個參數(shù)
- 調(diào)用時,函數(shù)的
)可以前置到倒數(shù)第二個參數(shù)末尾,后面的參數(shù)直接使用{ // 執(zhí)行代碼 },形式參數(shù)標(biāo)簽也隨之省略 - 將一個很長的閉包表達(dá)式作為最后一個參數(shù)傳遞給函數(shù),可以使用尾隨閉包來增強(qiáng)函數(shù)的可讀性
//尾隨閉包
func doSomething(info:String, clousre:(String)->Void){
clousre(info)
}
//不使用尾隨閉包進(jìn)行函數(shù)調(diào)用
doSomething(info: "Hello", clousre: { s in print(s) })
//使用尾隨閉包進(jìn)行函數(shù)調(diào)用
doSomething(info: "World") { s in
print(s)
}
逃逸閉包
- 閉包作為一個參數(shù)傳遞給一個函數(shù)
- 傳入函數(shù)的閉包如果在函數(shù)執(zhí)行結(jié)束之后才會被調(diào)用,那么這個閉包就叫做逃逸閉包
- 聲明一個接受閉包作為形式參數(shù)的函數(shù)時,可以在形式參數(shù)的類型之前寫上
@escaping來明確閉包是允許逃逸的 逃逸閉包會在函數(shù)結(jié)束后才執(zhí)行- 舉例
//逃逸閉包:閉包可以超出函數(shù)的范圍來調(diào)用
//存放沒有參數(shù)、沒有返回值的閉包
var closureArray :[()->Void] = [()->Void]()
//定義一個函數(shù),接收一個非逃逸閉包為參數(shù)
func nonEscapeClosure(closure:()->Void){
closure()
}
//定義一個函數(shù),接收一個逃逸閉包為參數(shù),將閉包并存儲到一個數(shù)組里面去,并沒有調(diào)用
func escapeClosure(closure: @escaping ()->Void){
print("函數(shù)開始")
closureArray.append(closure)
print("函數(shù)結(jié)束")
}
var x = 10
//打印10
print(x)
nonEscapeClosure {
x = 100
}
//打印100 因為閉包在函數(shù)里面執(zhí)行了
print(x)
escapeClosure {
x = 200
}
//打印100 因為閉包逃逸了 沒有在函數(shù)里面執(zhí)行
print(x)
closureArray.first?()
//打印200 在函數(shù)外面調(diào)用了閉包
print(x)
//尾隨閉包常用于異步回調(diào)
自動閉包
- 一種自動創(chuàng)建的閉包,用于包裝函數(shù)參數(shù)的表達(dá)式
- 不接受任何參數(shù),被調(diào)用時會返回被包裝在其中的表達(dá)式的值
- 在形式參數(shù)的類型之前加上
@autoclosure關(guān)鍵字標(biāo)識這是一個逃逸閉包
//自動閉包
func printIfTrue(predicate:@autoclosure ()->Bool){
if predicate() {
print("is true")
}
else{
print("is false")
}
}
//直接進(jìn)行調(diào)用了,Swift 將會把 2 > 1 這個表達(dá)式自動轉(zhuǎn)換為 () -> Bool。這樣我們就得到了一個寫法簡單、表意清楚的表達(dá)式。
printIfTrue(predicate: 2>1)
printIfTrue(predicate: 2<1)
閉包的循環(huán)引用
class NetworkTools: NSObject {
/// 完成回調(diào)屬性
var finishedCallBack: (()->())?
/// 加載數(shù)據(jù)
/// - parameter finished: 完成回調(diào)
func loadData(finished: () -> ()) {
self.finishedCallBack = finished
working()
}
func working() {
finishedCallBack?()
}
deinit {
print("網(wǎng)絡(luò)工具 88")
}
class ViewController: UIViewController {
var tools: NetworkTools?
override func viewDidLoad() {
super.viewDidLoad()
tools = NetworkTools()
tools?.loadData() {
print("加載數(shù)據(jù)完成,更新界面:", NSThread.currentThread())
weakSelf!.view.backgroundColor = UIColor.redColor()
}
}
/// 與 OC 中的 dealloc 類似,注意此函數(shù)沒有()
deinit {
print("控制器 88")
}
}
Swift中解決循環(huán)引用的方式
- 方案一:
- 使用weak,對當(dāng)前控制器使用弱引用
- 但是因為self可能有值也可能沒有值,因此weakSelf是一個可選類型,在真正使用時可以對其強(qiáng)制解包(該處強(qiáng)制解包沒有問題,因為控制器一定存在,否則無法調(diào)用所在函數(shù))
// 解決方案一:
weak var weakSelf = self
tools.loadData {
print("加載數(shù)據(jù)完成,更新界面:", NSThread.currentThread())
weakSelf!.view.backgroundColor = UIColor.redColor()
}
- 方案二:
- 和方案一類型,只是書寫方式更加簡單
- 可以寫在閉包中,并且在閉包中用到的self都是弱引用
tools.loadData {[weak self] () -> () in
print("加載數(shù)據(jù)完成,更新界面:", NSThread.currentThread())
self!.view.backgroundColor = UIColor.redColor()
}
- 方案三:
- 使用關(guān)鍵字unowned
- 從行為上來說 unowned 更像OC中的 unsafe_unretained
- unowned 表示:即使它原來引用的對象被釋放了,仍然會保持對被已經(jīng)釋放了的對象的一個 "無效的" 引用,它不能是 Optional 值,也不會被指向 nil
tools.loadData {[unowned self] () -> () in
print("加載數(shù)據(jù)完成,更新界面:", NSThread.currentThread())
self.view.backgroundColor = UIColor.redColor()
}