本文分兩部分介紹Block:
第一部分:Block基礎(chǔ)知識(shí)介紹
第二部分:Block經(jīng)常使用的三種情況(方法回調(diào),Cell點(diǎn)擊事件,VC之間逆向傳值)
第一部分:Block基礎(chǔ)知識(shí)介紹
Block就是大家常說的代碼塊,它可以傳值,可以封裝一段代碼,可以在任何時(shí)候執(zhí)行。
Block可以作為函數(shù)參數(shù)或者函數(shù)的返回值,而其本身又可以帶輸入?yún)?shù)或返回值。蘋果官方建議盡量多用block。在多線程、異步任務(wù)、集合遍歷、集合排序、動(dòng)畫轉(zhuǎn)場(chǎng)用的很多。
Block的定義:
在聲明的同時(shí)定義變量,然后賦值調(diào)用
int (^MySum)(int, int) = ^(int a, int b) {
return a+b;
};
NSLog(@"%d”,MySum(10, 11));
定義了一個(gè)叫MySum的Block對(duì)象,它帶有兩個(gè)int參數(shù),返回int。等式右邊就是Block的具體實(shí)現(xiàn)。最后調(diào)用Block,返回a+b的和。
也可用typedef先聲明類型,再定義變量進(jìn)行賦值
typedef int (^Sum) (int, int);
// 定義一個(gè)名叫sum的Block變量
Sum sum = ^(int a, int b) {
return a + b;
};
NSLog(@“%d", sum(10, 11));
Block可以訪問局部變量,但是不能修改:
int ?sum = 10;
int (^MyBlock)(int) = ^(int num) {
sum++;// 編譯報(bào)錯(cuò)
return num* sum;
};
如果要修改就要加關(guān)鍵字:__block,其實(shí)加上關(guān)鍵字,目的是將Block中截獲的變量拷貝到棧上,然后通過指針訪問變量。
__block int sum = 10;
int(^MyBlock)(int) = ^(int num) {
sum++;
return num* sum;
};
然而這樣的情況又是允許的:
NSMutableArray *array = [NSMutableArray array];
void (^Blo)() = ^(){
[array addObject:@"string"];
};
因?yàn)槲覀冎皇菍?duì)截獲的變量進(jìn)行了操作,而沒有進(jìn)行賦值。所以對(duì)于截獲變量,可以進(jìn)行操作而不可以進(jìn)行賦值。
Block和函數(shù)指針對(duì)比
其實(shí)Block和函數(shù)指針類似,都是通過指針訪問一段內(nèi)存代碼
定義函數(shù)指針
int (*myFn)();
定義Blocks
int (^MyBlocks)(int,int);
調(diào)用函數(shù)指針
(*myFn)(10, 20);
調(diào)用Blocks
MyBlocks(10, 20);
Block和函數(shù)指針區(qū)別
block的代碼是內(nèi)聯(lián)的,效率高于函數(shù)調(diào)用
block對(duì)于外部變量默認(rèn)是只讀屬性
block被Objective-C看成是對(duì)象處理
Block在內(nèi)存中的位置
根據(jù)Block在內(nèi)存中的位置分為三種類型NSGlobalBlock,NSStackBlock, NSMallocBlock。
NSGlobalBlock:類似函數(shù),位于text段;沒有使用Block以外的任何外部變量,或者當(dāng) block 字面量寫在全局作用域時(shí),Block不需要建立局部變量值的快照。
NSStackBlock:位于棧內(nèi)存,函數(shù)返回后Block將無效;使用了局部變量,局部變量當(dāng)前值被copy到棧上,作為常量供Block使用。
NSMallocBlock:位于堆內(nèi)存。Block修改了外部變量的值。
NSGlobalBlock
BlkSum blk1 = ^ long (int a, int b) {
return a + b;
};
NSStackBlock
int base = 100;
BlkSum blk2 = ^ long (int a, int b) {
return base + a + b;
};
NSMallocBlock
__block int base = 100;
BlkSum blk2 = ^ long (int a, int b) {
base++;
return base + a + b;
};
Block對(duì)不同類型變量的存取
局部變量:在Block中只讀。Block定義時(shí)copy變量的值,在Block中作為常量使用,所以即使變量的值在Block外改變,也不影響他在Block中的值。
int base = 100;
BlkSum sum = ^ long (int a, int b) {
return base + a + b;
};
base = 0;
printf("%ld\n",sum(1,2));// 這里輸出是103,而不是3
static變量、全局變量:如果把上個(gè)例子的base改成全局的、或static。Block就可以對(duì)他進(jìn)行讀寫了。因?yàn)槿肿兞炕蜢o態(tài)變量在內(nèi)存中的地址是固定的,Block在讀取該變量值的時(shí)候是直接從其所在內(nèi)存讀出,獲取到的是最新值,而不是在定義時(shí)copy的常量。
static int base = 100;
BlkSum sum = ^ long (int a, int b) {
base++;
return base + a + b;
};
base = 0;
printf("%d\n", base);// 0
printf("%ld\n",sum(1,2));// 這里輸出是4,而不是104
printf("%d\n", base);//1
輸出結(jié)果是0 4 1,表明Block外部對(duì)base的更新會(huì)影響B(tài)lock中的base的取值,同樣Block對(duì)base的更新也會(huì)影響B(tài)lock外部的base值。
Block變量:被__block修飾的變量稱作Block變量。 基本類型的Block變量等效于全局變量、或靜態(tài)變量。
第二部分:Block實(shí)踐
一:方法回調(diào)
常見的方法回調(diào),就是網(wǎng)絡(luò)請(qǐng)求中Block的使用。
網(wǎng)絡(luò)請(qǐng)求類.h文件
typedef void(^FFClientManagerBlock)(NSData *data, id response);
@interface HttpManager : NSObject
+ (HttpManager *)shareInstance;
- (void)requestCookQueryListWithMenu:(NSString *)menu
success:(FFClientManagerBlock)success
failuer:(FFClientManagerBlock)failure;
@end
網(wǎng)絡(luò)請(qǐng)求類.m文件
@implementation HttpManager
static HttpManager *instance = nil;
+(HttpManager *)shareInstance{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
- (void)requestCookQueryListWithMenu:(NSString *)menu
success:(FFClientManagerBlock)success
failuer:(FFClientManagerBlock)failure {
// 網(wǎng)絡(luò)請(qǐng)求代碼
}
@end
VC中調(diào)用:
[[HttpManager shareInstance] requestCookQueryListWithMenu:@"ID" success:^(NSData *data, id response) {
// success
} failuer:^(NSData *data, id response) {
// failuer
}];
二:Cell點(diǎn)擊事件:
如果你的Cell上有很多按鈕,那么你可能會(huì)在 cellForRowAtIndexPath 中 addTarget 多個(gè)事件,這樣無疑是很繁瑣的。以后可以這樣寫:
Cell的.h文件中:聲明Block和Block屬性
typedef void (^ButtonClick) (NSInteger tag, NSInteger row);
@property (copy, nonatomic) ButtonClick click;//在MRC下要用copy,ARC下可以用strong,因?yàn)橄到y(tǒng)做了一次copy操作
Cell的.m文件中:調(diào)用Block
- (IBAction)click:(id)sender {
UIButton *button = (UIButton *)sender;
self.click(button.tag, self.tag);
}
VC的.m文件中:
ButtonViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ButtonViewCell"];
// Configure the cell...
cell.tag = indexPath.row;
__weak? ViewController *weakSelf = self;
[cell setClick:^(NSInteger tag, NSInteger row) {
// 使用 weakSelf 防止循環(huán)引用
[weakSelf button:tag row:row];
}];
- (void)button:(NSInteger)tag row:(NSInteger)row {
NSLog(@"tag=%ld row=%ld", tag, row);
}
Swift:
Cell.swift文件
var click = { (tag: Int, row: Int) -> Void in }
Cell文件中調(diào)用閉包
@IBAction func click:(_ button: UIButton) {
? ? click(self.tag, button.tag);
}
VC.swift文件
let cell = tableView.dequeueReusableCell(withIdentifier: "ButtonViewCell", for: indexPath) as! ButtonViewCell
cell.tag = indexPath.row
cell.clickCount = { [weak self]
? ? row in
}
VC之間逆向傳值
從A頁面push到B頁面,從B頁面給A頁面?zhèn)髦禃r(shí),可以使用通知、代理,當(dāng)然也可以使用Block
A頁面的.m文件
UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
BViewController *vc = [storyBoard instantiateViewControllerWithIdentifier:@"BViewController"];
__weak? ViewController *weakSelf = self;
vc.block = ^(NSString *str, UIColor *color) {
NSLog(@"%@",str);
weakSelf.view.backgroundColor = color;
};
[self.navigationController pushViewController:vc animated:YES];
B頁面的.h文件中:聲明Block和Block屬性
typedef void(^Blo) (NSString *str, UIColor *color);
@property (copy, nonatomic) Blo block;
B頁面的.m文件中:調(diào)用Block
self.block(@"VC=B", [UIColor blackColor]);
由于文中的代碼比較簡(jiǎn)單,就不上傳Demo了,大家可以把代碼拷貝到自己的新建工程中,實(shí)際跑一下,看看效果。