iOS中Block的解析

一、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ì)被釋放掉。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容