IOS swift3.0 下閉包語法整理
一、閉包的概念
有oc基礎的都知道,閉包其實是oc裡面的block,語法格式不一樣,但作用是一樣的。主要是用於callBack(異步回調)或者兩個類之間的通信。它的本質一個函數,一個可執行的代碼塊,只是這個函數是沒有名字的,也就是匿名函數。你也可以把他看作如 int、float一樣,是一種數據類型,一種可以作為參數傳遞的數據類型。
二、基本語法
1、閉包的聲明
//定義一個求和閉包 //閉包類型:(Int,Int)->(Int) let add:(Int,Int)->(Int) = { (a,b) in return a + b; } //執行閉包,相當於調用函數 let result = add(1100, 200); //打印閉包返回值 print("result=\(result)");
閉包類型是由參數返回值決定,如上述add閉包類型為(Int,Int)->(Int),箭頭前面括號是參數類型,多個參數逗號隔開,箭頭後面括號返回值類型。
分析下上面代碼,“=”左邊的“ let add:(Int,Int)->(Int) ”意思是聲明一個add常量,add是一個閉包類型,並且這個閉包的類型是:(Int,Int)->(Int)。
“=”右邊是一個代碼塊,即閉包的具體實現,相當於給左邊add常量賦值。代碼塊的語法格式:
{ (參數1,參數2) in //code }
參數和需執行的代碼(code)用 關鍵字“in”隔開,如果閉包沒有參數, “ () in”可以直接省略:
{ //code }
你也可以用關鍵字“typealias”先聲明一個閉包的數據類型
import UIKit //聲明一個閉包類型 AddBlock typealias AddBlock = (Int,Int)->(Int); class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let add:AddBlock = { (a,b) in return a + b; } let result = add(1100, 200); print("result=\(result)"); } }
3、閉包的用法
1、兩個類之間的通信
ios中類之間的通信方式有多種,常用的有:協議代理、通知,以及本章要講的閉包。因為協議代理用起來比較麻煩,又是聲明協議方法、又要設置代理的,代碼步驟太多,我一般不用;通知一般用於兩個完全沒有關聯的類通信,可以一對多,但解耦和的太厲害,我一般是特定的場合用。所以針對有關聯的兩個類之間的通信,我一般是用閉包或block的,這樣比較簡潔迅速。
示例程序:監聽控制器上一個自定義view按鈕的點擊
界面效果
CustomView類中代碼
class CustomView: UIView { //聲明一個屬性btnClickBlock,type為閉包可選類型 //閉包類型:()->() ,無參數,無返回值 var btnClickBlock:(()->())?; //重寫 init(frame: CGRect)構造函數 override init(frame: CGRect) { super.init(frame:frame); //創建按鈕 let btn = UIButton(frame: CGRect(x: 15, y: 15, width: 80, height: 32)); btn.setTitle("按鈕", for: .normal); btn.backgroundColor = UIColor.blue; //綁定事件 btn.addTarget(self, action: #selector(CustomView.btnClick), for: .touchDown); //添加 addSubview(btn); } //按鈕點擊事件函數 func btnClick(){ if self.btnClickBlock != nil { //點擊按鈕執行閉包 //注意:屬性btnClickBlock是可選類型,需要先解包 self.btnClickBlock!(); } } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } }
Controller類中代碼:
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //創建CustomView對象 let cutomeView = CustomView(frame: CGRect(x: 50, y: 50, width: 200, height: 200)); //給cutomeView的btnClickBlock閉包屬性賦值 cutomeView.btnClickBlock = { // () in 無參數可以省略 //當按鈕被點擊時會執行此代碼塊 print("按鈕被點擊"); } cutomeView.backgroundColor = UIColor.yellow; //添加到控制器view上 self.view.addSubview(cutomeView); } }
2、異步回調(callBack)
以發送一個簡單的網絡請求為例:
/// 定義一個網絡請求函數 /// /// - parameter urlString: 請求接口 String /// - parameter succeed: 成功的回調 可選閉包 /// - parameter failure: 失敗的回調 可選閉包 func requestData(urlString:String,succeed: ((Any?)->(Void))?,failure:((Any?)->(Void))?){ let request = URLRequest(url: URL(string: urlString)!); //發送網絡請求 NSURLConnection.sendAsynchronousRequest(request, queue: OperationQueue()) { (_, data, error) in if error == nil { //請求成功,執行成功的回調,並把數據傳遞出去 succeed?(data); }else{ //請求失敗,執行失敗的回調,並把錯誤傳遞出去 failure?(error); } } }
// 調用函數requestData函數 requestData(urlString: "http://www.baidu.com", succeed: { (data) -> (Void) in //成功的回調 guard let result = data as? Data else{ return; } let srt = NSString(data: result, encoding: String.Encoding.utf8.rawValue); print(srt!) }) { (error) -> (Void) in //失敗的的回調 print(error); }
四、閉包的一些特殊語法
1、尾隨閉包
當閉包作為函數的最後一個參數時,可以省略前面的括號。尾隨閉包沒什麼特殊的作用,純粹是一種語法上的簡潔,增加易讀性。
例:定義一個函數:
//第二個參數:閉包 (String)->(Void) func post(url:String,succesce:(String)->Void) { print("發送請求"); succesce("請求完成"); }
執行函數,正常寫法:
//正常寫法,第二個參數,傳遞一個閉包 post("http", succesce: { //閉包傳遞的參數 (json) in //執行的代碼 print(json); });
執行函數,尾隨閉包寫法:
//尾隨閉包,當閉包作為函數的最後一個參數時,可以省略前面的括號 HttpTool.post("http") { (json) in print(json); };
2、逃逸閉包
看起來很“吊炸天”的一個名字,其實很簡單。當閉包作為一個參數傳遞到函數時,我們知道它一般是用於函數內部的異步回調,閉包是等異步任務完成以後才調用,而函數是會很快執行完畢並返回的,所以閉包它需要逃逸,以便稍後的回調。
逃逸閉包一般用於異步函數的回調,比如網絡請求成功的回調和失敗的回調。語法:在函數的閉包行參前加關鍵字“@escaping”。
或許細心的人已經發現我上面的示例網絡請求為什麼沒有出現關鍵字“@escaping”,你可以拉回去看下成功回調或失敗的回調,類型是“((Any?)->(Void))?”,後面帶了個“?”,這是閉包可選類型,並不是閉包類型,所以無需關鍵字“@escaping”。
假設成功和失敗的回調要弄成閉包類型,而你又要異步使用的話,那就要在形參前面加關鍵字,如下:
/// 定義一個網絡請求函數 /// /// - parameter urlString: 請求接口 String /// - parameter succeed: 成功的回調 閉包 因需要異步使用,前面加關鍵字@escaping修飾,指明其為逃逸閉包 /// - parameter failure: 失敗的回調 閉包 因需要異步使用,前面加關鍵字@escaping修飾,指明其為逃逸閉包 func requestData(urlString:String,succeed: @escaping (Any?)->(Void),failure:@escaping (Any?)->(Void)){ let request = URLRequest(url: URL(string: urlString)!); //發送網絡請求 NSURLConnection.sendAsynchronousRequest(request, queue: OperationQueue()) { (_, data, error) in if error == nil { //請求成功,執行成功的回調,並把數據傳遞出去 succeed(data); }else{ //請求失敗,執行失敗的回調,並把錯誤傳遞出去 failure(error); } } }
假設成功和失敗的回調要弄成閉包類型,而你又要異步使用的話,但你又不想在形參前面加關鍵字,那對不起,我也沒有辦法,編譯直接報錯!
感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!