要理解block和閉包需要和變量進行比較
首先在C中定義一個變量a:int a = 10;
定義一個block:
// 定義一個sumBlock
int(^sumBlock)(int, int) = ^(int a, int b) {
return a+b;
};
int c = sumBLock(1,2);// 使用block代碼
定義一個閉包
let sum:(Int, Int) -> Int = { (a, b) -> Int in
return a + b
}
print(sum(1,2))
從上面的定義可以看出:
block可以歸納為:
// 定義一個sumBlock
返回值類型(^block名字)(形參列表) = ^(實參列表) {
表達式
return 返回數(shù)據(jù);
};
返回類型 接收數(shù)據(jù)的變量名 = block名(實參列表);// 使用block代碼
閉包的一般表達式
let 閉包名:(形參列表) -> 返回值類型 = { (實參列表) -> 返回值類型 in
表達式
return 返回值
}
print(包名(實參列表)) // 使用閉包
Blcok相關(guān)語法
BLock類型變量在BLock語法下,一旦使用了BLock就相當于生成了可復制給BLock類型變量的值,Block既指源碼中給你的BLock語法也指有BLock生成的值
typedef int(^blk)(int); // 此時blk是一個BLock數(shù)據(jù)類型
blk bk = ^(int count){
return count + 1;
};
int (^blk1)(int) = bk;
int(^blk2)(int);
blk2 = blk1;
block會截獲局部變量
int a = 10;
void(^logBlock)(void) = ^(void) {
NSLog(@"a=%d",a);
};
a = 20;
logBlock();
// 輸出的是a=10
如果想在block中修改自動變量的值:
int val = 0;
void(^blk)(void) = ^{
val = 1;
};
blk();
printf("val = %d\n",val);
//會報錯
}
如果想在block中修改外部變量需要在自動變量加上__block修飾詞
__block iint val = 0;
void(^blk)(void) = ^{
val = 1;
};
blk();
printf("val is %d",val);
// 執(zhí)行結(jié)果是 val is 1
在以下代碼是不會報錯:
NSMutableArray *arrayM = [NSMutableArray array];
void(^blk)(void) = ^(void) {
id obj = [[NSObject alloc] init];
[arrayM addObject:obj];
};
blk();
如果外部變量沒有使用__block修飾,對該變量進行賦值就會報錯,但是對數(shù)組進行添加是沒有對數(shù)組進行賦值,是不會報錯。
block作為參數(shù)的時候:
- (void)modelWithBLock:(void(^)(int, int))plus number:(int)a title:(NSString* )title
{
}
// 定義一個變量和一個block
//int a;
// void(^plus)(int, int);
// NSString *title;
在使用的時候直接使用括號包裝起來,把變量名放到括號外面就好了
//(int)a
// (void(^)(int, int))plus
// (NSString*)title
閉包相關(guān)語法
利用typealias為閉包類型定義別名
typealias類似OC語法中的typedef
用法很簡單,直接用 = 賦值就行了.
// 定義一個沒有參數(shù)也沒有返回值的別名
typealias Nothing = () -> ()
// 如果沒有返回值也可以這樣寫
typealias Anything = () -> Void
// 接收一個參數(shù)沒有返回值
typealias PringtNumber = (Int) -> ()
// 有參數(shù),有返回值的
typealias Add = (Int, Int) -> (Int)
typealias Add = (_ num1: Int, _ num2: Int) -> (Int)
// 創(chuàng)建一個Add類型的閉包常量addCloser1
let addCloser1:Add
// 為已經(jīng)創(chuàng)建好的常量進行賦值
addCloser1 = {
(_ num1:Int, _ num2: Int) -> Int in
return num1 + num2
}
// 調(diào)用閉包并接收返回值
let result = addCloser1(1,2)
2.閉包類型申明和變量的穿件合并在一起
let Add:(_ num1: Int, _ num2: Int) -> (Int)
Add = {
(_ num: Int, _ num2: Int) -> (Int) in
return num + num2
}
// 調(diào)用閉包并接收返回值
let result = Add(2,3)
3.省略閉包接收的形參、省略閉包體中返回值
let Add:(Int,Int) -> (Int)
Add = {
(num,num2) -> (Int) in
return num + num2
}
// 調(diào)用閉包并接收返回值
let result = Add(2,3)
4.在3的基礎(chǔ)上繼續(xù)精簡
let Add:(Int, Int) -> (Int)
Add =
{ (num, num2) in
return num + num2
}
// 調(diào)用閉包并接收返回值
let result = Add(2,3)
5.如果閉包沒有參數(shù)可以省略in
// 創(chuàng)建一個 () -> (String)類型的閉包常量:addCloser1并賦值
let addCloser1:() -> (String) = {
return "這個閉包沒有參數(shù),但是有返回值"
}
// 調(diào)用閉包并接收返回值
let result = addCloser1()
6.簡寫的實際參數(shù)名
// 創(chuàng)建一個(String,String) -> (String) 類型的閉包常量:addCloser1并賦值
let addCloser1:(String, String) -> (String) = {
return "閉包的返回值是:\($0),\($1)"
}
// 調(diào)用閉包并接受返回值
let result = addCloser1("Hello", "Swift")
$0和$1分別表示閉包的第一個和第二個String類型的實際參數(shù)
閉包作為函數(shù)的參數(shù)
1.尾隨閉包
//函數(shù)的定義
func combie2(times: Int, handle:(String, String) -> (Void)) -> Void {
for i in 1...times {
handle("\(i)", "456")
}
}
// 函數(shù)的調(diào)用
combie2(times: 3) { (str1, str2) -> (Void) in
print("\(str1),\(str2)")
}
如果函數(shù)只有唯一的閉包參數(shù),沒有其他的參數(shù)可以省略函數(shù)的小括號
func combie2(handle:(String, String) -> (Void)) -> Void {
handle("123", "456")
}
combie2 { (str1, str2) -> (Void) in
print("\(str1), \(str2)")
}
2.逃逸閉包
后續(xù)補充。。。
在Swift中使用閉包不當可能引起循環(huán)強引用
class MainVC: UITabBarController {
var callBack:((String)->())?
override func viewDidLoad() {
super.viewDidLoad()
PringtString { (text) in
print(text)
// 閉包中捕獲self
self.view.backgroundColor = UIColor.red
}
}
func PringtString(callBack:@escaping(String) -> ()) {
callBack("這個閉包返回一段文字")
// 控制器self強引用著callBack
self.callBack = callBack;
}
解決辦法1、
class MainVC: UITabBarController {
var callBack:((String)->())?
override func viewDidLoad() {
super.viewDidLoad()
weak var weakSelf = self
PringtString { (text) in
print(text)
// 閉包中捕獲self
weakSelf?.view.backgroundColor = UIColor.red
}
}
func PringtString(callBack:@escaping(String) -> ()) {
callBack("這個閉包返回一段文字")
// 控制器self強引用著callBack
self.callBack = callBack;
}
可以使用weak進行修飾
解決辦法2、
可以在閉包的第一個大括號后面插入這段代碼[weak self],后面的代碼直接使用self?也能解決循環(huán)引用的問題
class MainVC: UITabBarController {
var callBack:((String)->())?
override func viewDidLoad() {
super.viewDidLoad()
// weak var weakSelf = self
PringtString { [weak self]
(text) in
print(text)
// 閉包中捕獲self
self?.view.backgroundColor = UIColor.red
}
}
func PringtString(callBack:@escaping(String) -> ()) {
callBack("這個閉包返回一段文字")
// 控制器self強引用著callBack
self.callBack = callBack;
}