一、Block的創(chuàng)建
創(chuàng)建block可以有兩種方式,如下代碼所示:
//第一種方式
//創(chuàng)建一個(gè)block類,創(chuàng)建規(guī)則為:
typedef returnType(^name)(arguments);
//沒(méi)有返回值,參數(shù)為name字符串。block類名為NameBlock
typedef void(^NameBlock)(NSString *name);
//返回值為int,參數(shù)為空。block類名為AgeBlock
typedef int (^AgeBlock)(void);
//返回值為字符串,參數(shù)為字符串name和age。block類名為DesBlock
typedef NSString *(^DesBlock)(NSString *name, NSInteger age);
第一種方式首先創(chuàng)建一個(gè)block類,然后通過(guò)類去創(chuàng)建對(duì)應(yīng)的block變量,下面是第一種方式執(zhí)行block代碼。
//代碼執(zhí)行到這里的時(shí)候并沒(méi)有執(zhí)行,只有當(dāng)調(diào)用這個(gè)block的時(shí)候才會(huì)執(zhí)行
NameBlock nameBlock = ^(NSString *name) {
NSLog(@"name:%@", name);
};
//執(zhí)行block
nameBlock(@"李四");
AgeBlock ageBlock = ^() {
return (NSInteger)12;
};
NSLog(@"age:%ld", ageBlock());
DesBlock desBlock = ^(NSString *name, NSInteger age) {
return [NSString stringWithFormat:@"姓名:%@,年齡:%ld", name, age];
};
NSLog(@"%@", desBlock(@"李四", 12));
第二種方式為直接創(chuàng)建block變量,如下代碼所示:
void (^nameBlock)(NSString *name) = ^(NSString *name) {
NSLog(@"name:%@", name);
};
nameBlock(@"李四");
int (^ageBlock)(void) = ^() {
return 12;
};
NSLog(@"%d", ageBlock());
NSString *(^desBlock)(NSString *name, NSInteger age) = ^(NSString *name, NSInteger age) {
return [NSString stringWithFormat:@"姓名:%@,年齡:%ld", name, age];
};
NSLog(@"%@", desBlock(@"李四", 12));
兩種方式創(chuàng)建出來(lái)的block是等同的,根據(jù)自己選擇使用任何一種都可以。
二、Block的使用
//從這段代碼可以看出來(lái),block代碼塊中的代碼執(zhí)行在block調(diào)用的時(shí)候,也就是說(shuō)創(chuàng)建block時(shí)候不執(zhí)行,當(dāng)調(diào)用到block的時(shí)候執(zhí)行。
//創(chuàng)建一個(gè)block變量的時(shí)候,會(huì)把block中的代碼塊存放到棧中。當(dāng)調(diào)用block的時(shí)候回去棧中找到這段代碼執(zhí)行。
void (^strBlock)(void) = ^() {
NSLog(@"執(zhí)行到這里1");
};
NSLog(@"執(zhí)行到這里2");
strBlock();
2018-07-03 11:31:56.431690+0800 BlockDemo[2118:129986] 執(zhí)行到這里2
2018-07-03 11:31:56.431882+0800 BlockDemo[2118:129986] 執(zhí)行到這里1
//從這段代碼可以看出來(lái),外部修改變量值;并不會(huì)影響到block內(nèi)部值的變化。
//這里是因?yàn)樵赽lock創(chuàng)建的時(shí)候是把a(bǔ)ge的值,傳遞到了block里面。相當(dāng)于把值復(fù)制一份傳遞進(jìn)去,外面的改變,并不會(huì)影響內(nèi)部的改變
NSInteger age = 25;
void (^ageBlock)(void) = ^() {
//age = 9; 這個(gè)代碼會(huì)報(bào)錯(cuò),不讓修改外部變量
NSLog(@"1:%ld", age);
};
NSLog(@"2:%ld", age);
age = 100;
NSLog(@"3:%ld", age);
ageBlock();
2018-07-03 14:09:11.329085+0800 BlockDemo[2353:174708] 2:22
2018-07-03 14:09:11.329178+0800 BlockDemo[2353:174708] 3:100
2018-07-03 14:09:11.329352+0800 BlockDemo[2353:174708] 1:22
//從這段代碼輸出的結(jié)果可以看出來(lái),外部改變了值block內(nèi)部值也發(fā)生了變化。
//這里是因?yàn)閎lock創(chuàng)建的時(shí)候是把desStr的指針傳遞到了block里面。里面和外面指向同一個(gè)內(nèi)存區(qū)域,當(dāng)外面值發(fā)生變化里面的值也會(huì)發(fā)生變化。因此block內(nèi)部使用指針變量也會(huì)造成引用計(jì)數(shù)的增加,所以block就存在循環(huán)引用的問(wèn)題
NSMutableString *desStr = [NSMutableString stringWithString:@"name:李四"];
void (^desBlock)(void) = ^{
NSLog(@"1:%@", desStr);
};
NSLog(@"2:%@", desStr);
[desStr appendString:@"age:30"];
NSLog(@"3:%@", desStr);
desBlock();
2018-07-03 15:47:52.997659+0800 BlockDemo[7249:385609] 2:name:李四
2018-07-03 15:47:52.997821+0800 BlockDemo[7249:385609] 3:name:李四age:30
2018-07-03 15:47:52.997989+0800 BlockDemo[7249:385609] 1:name:李四age:30
從上面兩個(gè)例子中我們可以總結(jié)出如下結(jié)論:
1.單純的值變量在block外部修改,并不影響block內(nèi)部的值。
2.指針變量修改block外部的值,會(huì)引起block內(nèi)部值的變化。
如果想要讓值變量也跟著外部修改而發(fā)生變化,就需要用到__block。請(qǐng)看下面代碼:
//__block關(guān)鍵字會(huì)讓block內(nèi)部的值變量跟隨外部的值變化而變化
//并且可以在block內(nèi)部修改外部變量的值
__block NSInteger sum = 45;
void (^sorceBlock)(void) = ^() {
//sum = 20; 這個(gè)代碼不會(huì)報(bào)錯(cuò)
NSLog(@"1:%ld", sum);
};
NSLog(@"2:%ld", sum);
sum = 99;
NSLog(@"3:%ld", sum);
sorceBlock();
2018-07-03 16:07:54.437503+0800 BlockDemo[7482:437669] 2:45
2018-07-03 16:07:54.437707+0800 BlockDemo[7482:437669] 3:99
2018-07-03 16:07:54.438430+0800 BlockDemo[7482:437669] 1:99
三、block反向傳值
通過(guò)點(diǎn)擊跳轉(zhuǎn)按鈕跳轉(zhuǎn)到TwoViewController界面
TowViewController *towVC = [[TowViewController alloc] init];
//前面我們講過(guò)如果外部創(chuàng)建的對(duì)象在block內(nèi)部使用,會(huì)把對(duì)象的指針傳遞進(jìn)去。造成引用計(jì)數(shù)的增加這里就是出現(xiàn)了這種情況,出現(xiàn)這種情況的結(jié)果是會(huì)造成循環(huán)引用,內(nèi)存不能被釋放。
//1.twoVC.stringName表示這個(gè)controller持有一份當(dāng)前block內(nèi)容。
//2.block中的參數(shù)string表示為controller中的self.towText,所以block也持有一份controller
//3.這樣就造成了雙方互相持有,從而導(dǎo)致循環(huán)引用內(nèi)存不能夠被釋放
towVC.stringName = ^(NSString *string) {
self.oneText.text = string;
};
[self.navigationController pushViewController:towVC animated:YES];
在TwoViewController界面通過(guò)點(diǎn)擊返回按鈕,返回到前一個(gè)界面并且把需要傳遞的值傳遞出去。
//在.h中聲明block
typedef void(^blockString)(NSString *string);
@interface TowViewController : UIViewController
//@property (nonatomic, strong) NSString *string;
//這里為什么使用copy
//1.strong 表示在棧創(chuàng)建了一block變量,但是block是延后執(zhí)行這一特點(diǎn),會(huì)造成這個(gè)block變量被置為nil
//2.棧的內(nèi)存空間,是系統(tǒng)決定收回的。前面開(kāi)辟空間創(chuàng)建的變量,由于創(chuàng)建變量時(shí)空間不夠,系統(tǒng)就會(huì)回收前面的變量空間
//3.copy 表示把block內(nèi)存區(qū)域里面的東西,復(fù)制一份到堆中。
//4.堆的內(nèi)存空間,是由程序員來(lái)控制的。開(kāi)辟和回收空間都是由程序員自己操作。
@property (nonatomic, copy) blockString stringName;
@end
//在.m中實(shí)現(xiàn)跳轉(zhuǎn)方法
- (IBAction)endActionBtn:(id)sender {
//這里的判斷是為了防止block為空的情況
if (self.stringName) {
self.stringName(self.towText.text);
}
[self.navigationController popViewControllerAnimated:YES];
}
上面的代碼就會(huì)出現(xiàn)循環(huán)引用的問(wèn)題,如果想要解決這個(gè)問(wèn)題就要用到__weak關(guān)鍵字,具體代碼如下所示:
- (IBAction)endActionBtn:(id)sender {
//把self變?yōu)槿踔羔? __weak typeof(self) weakSelf = self;
//這里的判斷是為了防止block為空的情況
if (self.stringName) {
//防止代碼執(zhí)行的過(guò)程中self被釋放掉,從而導(dǎo)致代碼崩潰
__strong typeof(self) strongSelf = weakSelf;
self.stringName(strongSelf.towText.text);
}
[self.navigationController popViewControllerAnimated:YES];
}
接下來(lái)解釋一下為什么使用__weak修飾之后就可以打破循環(huán)引用。是因?yàn)槭褂胈_weak表示弱引用,不能夠造成引用計(jì)數(shù)的增加。因此block內(nèi)部使用weakSelf就不會(huì)造成引用循環(huán),當(dāng)block釋放之后,block內(nèi)部的內(nèi)容也都會(huì)被釋放掉。