swift_040(Swift閉包宣告與用途)
一、閉包的概念
閉包其實是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);
}
}
}