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