亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > Swift > 正文

Swift 中閉包的簡單使用

2020-03-09 17:47:18
字體:
來源:轉載
供稿:網友

本文主要是介紹Swift中閉包的簡單使用,將從“閉包的定義”、"閉包的創建、賦值、調用"、“閉包常見的幾種使用場景”,"使用閉包可能引起的循環強引用" 四個方面入手,重點介紹閉包如何使用,沒有高深的概念,只是專注于實際使用,屬于入門級水平,后面還會有關于閉包更加詳細和深入理解的文章。希望大家在閱讀完本文后能夠對閉包有一個整體的理解以及能夠簡單的使用它。

閉包的定義

在Swift開發文檔中是這樣介紹閉包的:閉包是可以在你的代碼中被傳遞和引用的功能性獨立模塊。Swift 中的閉包和 C 以及 Objective-C 中的 block 很像,還有其他語言中的匿名函數也類似。閉包的作用主要是:夠捕獲和存儲定義在其上下文中的任何常量和變量的引用, 能夠為你處理所有關于捕獲的內存管理的操作(概念性問題,可以不用糾結太多啦)。

閉包的表達式語法

閉包表達式語法有如下的一般形式:

 { (parameters/接收的參數) -> (return type/閉包返回值類型) in  statements/保存在閉包中需要執行的代碼 }

閉包根據你的需求是有類型的,閉包的類型 一般形式如下:

(parameters/接收的參數) -> (return type/閉包返回值類型)

利用typealias為閉包類型定義別名

這里先介紹一下 typealias的使用 : typealias是Swift中用來為已經存在的類型重新定義名字的關鍵字(類似于OC語法中的 typedef),重新命名的新名字用來替代之前的類型,并且能夠使代碼變得更加清晰簡單容易理解。typealias 的用法很簡單,直接用 = 賦值就可以了:

typealias <type name> = <type expression>

這里我們可以用 typealias 來為看似較為復雜的閉包類型定義別名,這樣以后我們就可以用別名直接去申明這樣類型的閉包了,例子如下:

//為沒有參數也沒有返回值的閉包類型起一個別名 typealias Nothing = () -> () //如果閉包的沒有返回值,那么我們還可以這樣寫, typealias Anything = () -> Void //為接受一個Int類型的參數不返回任何值的閉包類型 定義一個別名:PrintNumber typealias PrintNumber = (Int) -> () //為接受兩個Int類型的參數并且返回一個Int類型的值的閉包類型 定義一個別名:Add typealias Add = (Int, Int) -> (Int)

閉包是否接受參數、接受幾個參數、返回什么類型的值完全取決于你的需求。

閉包的創建、賦值、調用

閉包表達式語法能夠使用常量形式參數、變量形式參數和輸入輸出形式參數,但不能提供默認值??勺冃问絽狄材苁褂?,但需要在形式參數列表的最后面使用。元組也可被用來作為形式參數和返回類型。在閉包的中會用到一個關鍵字in,in 可以看做是一個分割符,他把該閉包的類型和閉包的函數體分開,in前面是該閉包的類型,in后面是具體閉包調用時保存的需要執行的代碼。表示該閉包的形式參數類型和返回類型定義已經完成,并且閉包的函數體即將開始執行。這里總結了一下可能用到的幾種形式實現閉包的創建、賦值、調用的過程。例子如下:

方式一:利用typealias最完整的創建

//為(_ num1: Int, _ num2: Int) -> (Int) 類型的閉包定義別名:Add typealias Add = (_ num1: Int, _ num2: Int) -> (Int)//創建一個 Add 類型的閉包常量:addCloser1 let addCloser1: Add//為已經創建好的常量 addCloser1 賦值 addCloser1 = {  (_ num1: Int, _ num2: Int) -> (Int) in  return num1 + num2 }//調用閉包并接受返回值 let result = addCloser1(20, 10)

形式二:閉包類型申明和變量的創建合并在一起

//創建一個 (_ num1: Int, _ num2: Int) -> (Int) 類型的閉包常量:addCloser1 let addCloser1: (_ num1: Int, _ num2: Int) -> (Int)//為已經創建好的常量 addCloser1 賦值 addCloser1 = {   (_ num1: Int, _ num2: Int) -> (Int) in   return num1 + num2 } //調用閉包并接受返回值 let result = addCloser1(20, 10)

形式三:省略閉包接收的形參、省略閉包體中返回值

//創建一個 (Int, Int) -> (Int) 類型的閉包常量:addCloser1 let addCloser1: (Int, Int) -> (Int)//為已經創建好的常量 addCloser1 賦值 addCloser1 = {   (num1, num2) in   return num1 + num2 }//調用閉包并接受返回值 let result = addCloser1(20, 10)

形式四:在形式三的基礎上進一步精簡

//創建一個 (Int, Int) -> (Int) 類型的閉包常量:addCloser1 并賦值 let addCloser1: (Int, Int) -> (Int) = {   (num1, num2) in   return num1 + num2  } //調用閉包并接受返回值 let result = addCloser1(20, 10)

形式五:如果閉包沒有接收參數省略in

//創建一個 () -> (String) 類型的閉包常量:addCloser1 并賦值  let addCloser1: () -> (String) = {   return "這個閉包沒有參數,但是有返回值"   } //調用閉包并接受返回值  let result = addCloser1()

形式六:簡寫的實際參數名

//創建一個 (String, String) -> (String) 類型的閉包常量:addCloser1 并賦值  let addCloser1: (String, String) -> (String) = {   return "閉包的返回值是:/($0),/($1)"   } //調用閉包并接受返回值  let result = addCloser1("Hello", "Swift!")

說明: 得益于Swift的類型推斷機制,我們在使用閉包的時候可以省略很多東西,而且Swift自動對行內閉包提供簡寫實際參數名,你也可以通過 $0, $1, $2 等名字來引用閉包的實際參數值。如果你在閉包表達式中使用這些簡寫實際參數名,那么你可以在閉包的實際參數列表中忽略對其的定義,并且簡寫實際參數名的數字和類型將會從期望的函數類型中推斷出來。in關鍵字也能被省略,$0 和 $1 分別是閉包的第一個和第二個 String類型的 實際參數(引自文檔翻譯)。

閉包常見的幾種使用場景

基本掌握閉包的概念后,我們就可以利用閉包做事情了,下面介紹一下閉包在開發中的可能被用到的場景。
場景一:利用閉包傳值
開發過程中常常會有這樣的需求:一個頁面的得到的數據需要傳遞給前一個頁面使用。這時候使用閉包可以很簡單的實現兩個頁面之間傳值。

Swift,閉包,閉包詳解,閉包實例
圖片發自簡書App

場景再現:

第一個界面中有一個用來顯示文字的UILabel和一個點擊進入到第二個界面的UIButton,第二個界面中有一個文本框UITextField和一個點擊返回到上一個界面的UIButton,現在的需求是在第二個界面的UITextField中輸入完文字后,點擊返回按鈕返回到第一個界面并且將輸入的文字顯示在第一個界面(當前頁面)的UILabel中。

實現代碼:

首先在第二個界面的控制器中定義一個( String) -> ()可選類型的閉包常量closer作為SecondViewController的屬性。closer接收一個String類型的參數(就是輸入的文字)并且沒有返回值。然后在返回按鈕的點擊事件中傳遞參數執行閉包。

import UIKitclass SecondViewController: UIViewController { //輸入文本框 @IBOutlet weak var textField: UITextField! //為創建一個(String) -> () 的可選類型的閉包變量作為控制器的屬性 var closer: ((String) -> ())? //返回按鈕的點擊事件 @IBAction func backButtonDidClick(_ sender: AnyObject) {  //首先判斷closer閉包是否已經被賦值,如果已經有值,直接調用該閉包,并將輸入的文字傳進去。  if closer != nil {   closer!(textField.text!)  }  navigationController?.popViewController(animated: true) }}

這里有一個注意點:我們在為SecondViewController定義變量閉包屬性的時候需要將類型申明為可選類型,閉包可選類型應該是((String) -> ())?而不是(String) -> ()?的,后者指的是閉包的返回值是可選類型。

回到第一個界面的控制器中,我們需要拖線拿到UILabel的控件,然后重寫prepare(for segue: UIStoryboardSegue, sender:Any?) { }方法,在這個跳轉方法中拿到跳轉的目標控制器SecondVC并為他的閉包屬性賦值,當然如果你的跳轉按鈕的點擊事件是自己處理的,直接在按鈕的點擊事件中這樣做就OK了。

import UIKitclass FirstViewController: UIViewController { //顯示文字的label @IBOutlet weak var label: UILabel! //重寫這個方法 override func prepare(for segue: UIStoryboardSegue, sender: Any?) {  //拿到跳轉的目標控制器  let secondVC = segue.destination as! SecondViewController  //為目標控制器的閉包屬性賦值  secondVC.closer = {   //將閉包的參數(輸入的文本內容)顯示在label上   self.label.text = $0  } }}

經過上面的處理,我們就可以實現兩個頁面之間的傳值了(是不是很簡單呢),當然在具體的開發中很可能不是傳遞文本內容這么簡單,當需要傳遞更復雜的值時,我們可以將傳遞的值包裝成一個模型,直接用閉包傳遞模型就好了。

場景二:閉包作為函數的參數

在OC語法中block可以作為函數的參數進行傳遞,在Swift中同樣可以用閉包作為函數的參數,還記得上面利用typealias關鍵字定義別名嗎,定義完的別名就是一個閉包類型,可以用它申明一個閉包常量或變量當做參數進行傳遞。一個最簡單的閉包作為函數參數例子如下:

//為接受一個Int類型的參數并且返回一個Int類型的值的閉包類型定義一個別名:Number typealias Number = (num1: Int) -> (Int) //定義一個接收Number類型的參數沒有返回值的方法 func Text(num: Number) {  //code }

閉包在作為函數的參數進行傳遞的時候根據函數接收參數的情況有很多種不同的寫法。這里我們主要介紹一下尾隨閉包的概念。

首先看一下一般形式的閉包作為函數的參數傳遞:

//拼接兩個字符串和一個整數 func combine(handle:(String, String) -> (Void), num: Int) {   handle("hello", "world /(num)")  }//方法調用 combine(handle: { (text, text1) -> (Void) in   print("/(text) /(text1)")   }, num: 2016)

可以看到上面的combine方法在主動調用的時候依舊是按照func(形參: 實參)這樣的格式。當我們把閉包作為函數的最后一個參數的時候就引出了尾隨閉包的概念。

一,尾隨閉包

尾隨閉包是指當需要將一個很長的閉包表達式作為函數最后一個實際參數傳遞給函數時,一個書寫在函數形式參數的括號外面(后面)的閉包表達式:

 func combine1(num:Int, handle:(String, String)->(Void)) {   handle("hello", "world /(num)") } combine1(num: 2016) { (text, text1) -> (Void) in   print("/(text) /(text1)") }

進一步:如果閉包表達式被用作函數唯一的實際參數并且你把閉包表達式用作尾隨閉包,那么調用這個函數的時候函數名字的()都可以省略:

 func combine2(handle:(String, String)->(Void)) {   handle("hello", "world") } combine2 { (text, text1) -> (Void) in   print("/(text) /(text1)") }

二,逃逸閉包

如果一個閉包被作為一個參數傳遞給一個函數,并且在函數return之后才被喚起執行,那么我們稱這個閉包的參數是“逃出”這個函數體外,這個閉包就是逃逸閉包。此時可以在形式參數前寫 @escaping來明確閉包是允許逃逸的。
閉包可以逃逸的一種方法是被儲存在定義于函數外的變量里。比如說,很多函數接收閉包實際參數來作為啟動異步任務的回調。函數在啟動任務后返回,但是閉包要直到任務完成——閉包需要逃逸,以便于稍后調用。用我們最常用的網絡請求舉例來說:

func request(methodType:RequestMethodType, urlString: String, parameters: [String : AnyObject], completed: @escaping (AnyObject?, NSError?) -> ()) {  // 1.封裝成功的回調  let successCallBack = { (task : URLSessionDataTask?, result : Any?) -> Void in   completed(result as AnyObject?, nil)  }  // 2.封裝失敗的回調  let failureCallBack = { (task : URLSessionDataTask?, error : Error?) -> Void in   completed(nil, error as NSError?)  }  //判斷是哪種請求方式  if methodType == .get {   get(urlString, parameters: parameters, success: successCallBack, failure: failureCallBack)  } else {   post(urlString, parameters: parameters, success: successCallBack, failure: failureCallBack)  } }

這里的completed閉包被作為一個參數傳遞給request函數,并且在函數調用get或post后才會被調用。

使用閉包可能引起的循環強引用
Swift中不當的使用閉包可能會引起循環強引用,之所以稱之為“強”引用,是因為它會將實例保持住,只要強引用還在,實例是不允許被銷毀的。循環強引用會一直阻止類實例的釋放,這就在你的應用程序中造成了內存泄漏。
舉個例子:

import UIKitclass ThirdViewController: UIViewController { var callBack: ((String) -> ())? override func viewDidLoad() {  super.viewDidLoad()  printString { (text) in   print(text)   //閉包中捕獲了self   self.view.backgroundColor = UIColor.red  } } func printString(callBack:@escaping (String) -> ()) {  callBack("這個閉包返回一段文字")  //控制器強引用于著callBack  self.callBack = callBack } deinit {  print("ThirdViewController---釋放了") }}

Swift,閉包,閉包詳解,閉包實例

當你在定義printString這個方法時執行self.callBack = callBack代碼實際上是self對callBack閉包進行了強引用,到這里其實并沒有產生循環引用,但是當你在調用printString方法的閉包里面又訪問了self.view.backgroundColor屬性,此時強引用就發生了,即self引用了callBack,而callBack內部又引用著self,誰都不愿意松手,我們就說這兩者之間產生了循環強引用。

使用閉包何時會出現循環強引用 :

當你把一個閉包分配給類實例屬性的時候,并且這個閉包中又捕獲了這個實例。捕獲可能發生于這個閉包函數體中訪問了實例的某個屬性,比如 self.someProperty ,或者這個閉包調用了一個實例的方法,例如 self.someMethod() 。這兩種情況都導致了閉包捕獲了self ,從而產生了循環強引用。

閉包循環引用的本質是:

閉包中循環強引用的產生,是因為閉包和類相似(還有一種兩個類實例之間的循環強引用),都是引用類型。當你把閉包賦值給了一個屬性,你實際上是把一個引用賦值給了這個閉包。兩個強引用讓彼此一直有效。

如何解決閉包的循環強引用:

方式一:類似于OC中使用__weak解決block的循環引用,Swift中支持使用weak關鍵字將類實例聲明為弱引用類型(注意,弱引用類型總是可選類型),打破類實例對閉包的強引用,當對象銷毀之后會自動置為nil,對nil進行任何操作不會有反應。

import UIKitclass ThirdViewController: UIViewController { var callBack: ((String) -> ())? override func viewDidLoad() {  super.viewDidLoad()  //將self申明為弱引用類型,打破循環引用  weak var weakSelf = self  printString { (text) in   print(text)   //閉包中鋪捕獲了self   weakSelf?.view.backgroundColor = UIColor.red  } } func printString(callBack:@escaping (String) -> ()) {  callBack("這個閉包返回一段文字")  //控制器強引用于著callBack  self.callBack = callBack } deinit {  print("ThirdViewController---釋放了") }}

方式二:作為第一種方式的簡化操作,我們可以在閉包的第一個大括號后面緊接著插入這段代碼[weak self],后面的代碼直接使用self?也能解決循環引用的問題。

import UIKitclass ThirdViewController: UIViewController {  var callBack: ((String) -> ())?  override func viewDidLoad() {    super.viewDidLoad()    printString {[weak self] (text) in      print(text)      self?.view.backgroundColor = UIColor.red    }  }  func printString(callBack:@escaping (String) -> ()) {    callBack("這個閉包返回一段文字")    //控制器強引用于著callBack    self.callBack = callBack  }  deinit {    print("ThirdViewController---釋放了")  }}

方式三:在閉包和捕獲的實例總是互相引用并且總是同時釋放時,可以將閉包內的捕獲定義為無主引用unowned。

import UIKitclass ThirdViewController: UIViewController {  var callBack: ((String) -> ())?  override func viewDidLoad() {    super.viewDidLoad()    printString {[unowned self] (text) in      print(text)      self?.view.backgroundColor = UIColor.red    }  }  func printString(callBack:@escaping (String) -> ()) {    callBack("這個閉包返回一段文字")    //控制器強引用于著callBack    self.callBack = callBack  }  deinit {    print("ThirdViewController---釋放了")  }}

注意:unowned是Swift中另外一種解決循環引用的申明無主引用類型的關鍵字,類似于OC中的__unsafe_unretained;大家都知道__weak和__unsafe_unretained的相同點是可以將該關鍵字修飾的對象變成弱引用解決可能存在的循環引用。不同點在于前者修飾的對象如果發現被銷毀,那么指向該對象的指針會立即指向nil,而__unsafe_unretained修飾的對象如果發現被銷毀,指向該對象的指針依然指向原來的內存地址,如果此時繼續訪問該對象很容易產生壞內存訪問/野指針/僵尸對象訪問。
同樣的道理Swift中也是一樣的。和弱引用類似,無主引用不會牢牢保持住引用的實例。但是不像弱引用,總之,無主引用假定是永遠有值的。因此,無主引用總是被定義為非可選類型。你可以在聲明屬性或者變量時,在前面加上關鍵字unowned 表示這是一個無主引用。由于無主引用是非可選類型,你不需要在使用它的時候將它展開。無主引用總是可以直接訪問。不過 ARC 無法在實例被釋放后將無主引用設為 nil ,因為非可選類型的變量不允許被賦值為 nil 。如果此時繼續訪問已經被釋放實例很容易產生壞內存訪問/野指針/僵尸對象訪問。

所以Swift建議我們如果被捕獲的引用永遠不為 nil ,應該用unowned而不是weak,相反,如果你不確定閉包中捕獲的引用是不是存在為nil的可能,你應該使用weak。

以上的代碼是根據最新的Swift3.0語法編寫的,經本人在Xcode8.0、iOS10.0環境下編譯通過。有任何疑問歡迎在評論區留言,感覺大家的閱讀。


注:相關教程知識閱讀請移步到swift教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
夜夜嗨av一区二区三区四区| 精品美女国产在线| 日韩一区二区三区国产| 日韩av色综合| 欧美精品在线视频观看| 亚洲国产天堂久久综合网| www.美女亚洲精品| 韩国三级日本三级少妇99| 日韩亚洲欧美中文高清在线| 91香蕉嫩草神马影院在线观看| 久久91精品国产91久久跳| 亚洲黄色免费三级| 日韩视频在线观看免费| 国产成+人+综合+亚洲欧美丁香花| 91在线视频免费| 欧美孕妇孕交黑巨大网站| 国精产品一区一区三区有限在线| 国产精品九九九| 韩国精品美女www爽爽爽视频| 91亚洲精品一区二区| 亚洲国产精品久久精品怡红院| 亚洲区免费影片| 一区二区欧美亚洲| 九九热这里只有在线精品视| 亚洲白拍色综合图区| 国产精品视频在线播放| 亚洲成人中文字幕| 97国产suv精品一区二区62| 欧美成人精品一区二区| 国产在线视频2019最新视频| 亚洲福利视频网| 17婷婷久久www| 国产精品亚洲综合天堂夜夜| 欧美午夜视频在线观看| 亚洲国产精品网站| 亚洲欧美中文字幕在线一区| 久久影院模特热| 狠狠躁18三区二区一区| 日韩综合视频在线观看| 91精品久久久久久久久久入口| 国产精品久久9| 欧美成人一区二区三区电影| 日本一本a高清免费不卡| 777777777亚洲妇女| 欧美精品久久久久久久久| 韩曰欧美视频免费观看| 国产日韩欧美日韩| 亚洲香蕉成人av网站在线观看| 国模精品视频一区二区| 国产在线a不卡| 日韩亚洲欧美中文高清在线| 国产成人福利网站| 国产成人精彩在线视频九色| 亚洲精品白浆高清久久久久久| 欧美日韩色婷婷| 日本韩国在线不卡| 午夜精品久久久99热福利| 亚洲人成亚洲人成在线观看| 亚洲美女av黄| 中文字幕av一区二区三区谷原希美| 精品自拍视频在线观看| 一区二区三区天堂av| 久久精品视频99| 97香蕉超级碰碰久久免费的优势| 中文字幕亚洲一区| 日韩美女视频在线观看| 亚洲午夜色婷婷在线| 久久躁日日躁aaaaxxxx| 欧美日韩性视频在线| 欧美一区二区三区免费观看| 日韩精品极品视频| 97人人爽人人喊人人模波多| 中文字幕亚洲字幕| 久久久www成人免费精品| 亚洲自拍偷拍视频| 欧美激情a∨在线视频播放| 久久久久国产精品免费网站| 欧美日韩国产成人在线观看| 97精品一区二区三区| 91九色蝌蚪国产| 国产精品∨欧美精品v日韩精品| 国产第一区电影| 国产啪精品视频| 亚洲精品国产精品国自产观看浪潮| 国产精品一区二区久久国产| 日韩中文字幕视频在线观看| 亚洲一区二区三区在线视频| 久久最新资源网| 亚洲国产日韩欧美在线动漫| 国产精品夜色7777狼人| 欧美精品videosex性欧美| 日韩av在线导航| 国产精品自产拍高潮在线观看| 亚洲国产中文字幕在线观看| 日韩美女在线播放| 国产日韩欧美一二三区| 欧美激情国产日韩精品一区18| 91精品国产网站| 欧美一性一乱一交一视频| 久久99国产精品自在自在app| 91精品久久久久久久久久久久久| 美女性感视频久久久| 国产精品高潮呻吟久久av无限| 欧美在线激情网| 日韩精品在线免费播放| 欧美日韩加勒比精品一区| 中文字幕日韩视频| 日韩av电影在线免费播放| 18一19gay欧美视频网站| 亚洲视屏在线播放| 欧美整片在线观看| 国产精品久久久久久久7电影| 亚洲男人天堂2023| 九九久久久久久久久激情| 亚洲激情成人网| 91av国产在线| 亚洲欧美制服综合另类| 亚洲一区二区三区香蕉| 成人国产精品久久久| 亚洲香蕉成视频在线观看| 精品国模在线视频| 狠狠躁夜夜躁人人爽超碰91| xxxxx成人.com| 日韩黄色高清视频| 成人黄色影片在线| 精品国产欧美一区二区三区成人| 国产精品久久久久久久久久99| 国模私拍视频一区| 国产精品久久9| 日韩美女av在线免费观看| 最新国产成人av网站网址麻豆| 亚洲理论电影网| 都市激情亚洲色图| 91精品国产免费久久久久久| 国产精品国产三级国产aⅴ9色| 国产午夜精品免费一区二区三区| 正在播放亚洲1区| 日韩激情av在线免费观看| 久久男人的天堂| 69精品小视频| 欧美精品一区在线播放| 久久久999精品| 亚洲国产成人av在线| 88xx成人精品| 国产精品成熟老女人| 国产91精品久久久久久久| 亚洲欧美日韩国产中文专区| 国产精品国产福利国产秒拍| 精品久久久国产精品999| 久久青草精品视频免费观看| 亚洲精品二三区| 国产精品91久久久久久| 亚洲美女动态图120秒| 国产成人在线视频| 亚洲欧美日韩天堂一区二区| 成人亚洲欧美一区二区三区| 亚洲欧洲一区二区三区在线观看| 97色在线视频| 欧美激情性做爰免费视频| 国产精品视频xxxx| 97视频在线观看亚洲| 国产欧美精品一区二区三区-老狼| 国产亚洲xxx| 亚洲成人精品视频|