Block學(xué)習(xí)總結(jié)(一)

Block

Block作為日常開(kāi)發(fā)中必備的一種開(kāi)發(fā)技巧,通過(guò)日常的使用和學(xué)習(xí),在此對(duì) Block的基本概念,使用語(yǔ)法和 對(duì)于外部變量的使用三方面做一個(gè)學(xué)習(xí)總結(jié)。

1.什么是Blocks

什么是blocks,blocks是c語(yǔ)言的一種擴(kuò)充功能。可以解釋為“帶有自動(dòng)變量的匿名函數(shù)”。

匿名函數(shù)

匿名函數(shù)就是指沒(méi)有名稱的函數(shù)。正常的函數(shù)表達(dá)式一般為:

返回值類型 函數(shù)名 返回值 {

}
void func () {
    
}

而當(dāng)我門需要調(diào)用這個(gè)函數(shù)時(shí),是必須要知道函數(shù)名的。通常調(diào)用函數(shù)的方式有2種:

1 函數(shù)名()

2 函數(shù)指針的方式調(diào)用

// 1 函數(shù)名調(diào)用
   func();

// 2 函數(shù)指針調(diào)用
    void(*funcptr)() = &func;
    funcptr();

而blocks的函數(shù)表達(dá)式并沒(méi)有帶函數(shù)名:

^ 返回值 參數(shù) {

}

^ void(){
        
};

把blocks賦值給block類型的參數(shù)

void(^blk)() = ^ void(){
        
};

調(diào)用blocks
blk();
或者
^ void(){
        
}();

可以發(fā)現(xiàn)blocks函數(shù)與常見(jiàn)的函數(shù)的區(qū)別 是沒(méi)有了函數(shù)名和 最前面加上了** ^ *符號(hào)。而block類型的變量和函數(shù)指針的區(qū)別只是把函數(shù)指針的號(hào)換成了^。

截獲自動(dòng)變量

帶有自動(dòng)變量其實(shí)可以解釋為能夠截取自動(dòng)變量的值。通常的變量類型有

  • 局部變量(自動(dòng)變量)
  • 全局變量
  • 全局靜態(tài)變量
  • 靜態(tài)變量

能夠在函數(shù)的多次調(diào)用的時(shí)候傳遞的變量值只有:

  • 全局變量
  • 全局靜態(tài)變量
  • 靜態(tài)變量
void test() {
     int a = 10;
     void(^blk)() = ^void(){
         printf("a = %d",a);
     };
     a = 20;
     blk();
}

函數(shù)最后的打印結(jié)果為*** a = 10 ***。到這里可以解釋出blocks為帶有(截獲)自動(dòng)變量i的匿名函數(shù)。

2.Block語(yǔ)法

block函數(shù)

關(guān)于block函數(shù)可以表示為 ^ 返回值類型 參數(shù) 表達(dá)式。

而以下4種方式也是可以的

1.^ 返回值類型 參數(shù) 表達(dá)式
    ^int(){
    
        return 1;
    };
     
2.^ 參數(shù) 表達(dá)式   (省略返回值類型)
    ^(){
        return 1;
    };

3.^ 返回值類型 表達(dá)式   (在沒(méi)有參數(shù)的情況下省略參數(shù))        
    ^int{
        return 1;
    };

4.^ 表達(dá)式   (返回值類型和參數(shù) 都可以省略)        
    ^{
        return 1;
    };

block類型的變量

在之前介紹過(guò)block類型的變量,將block函數(shù)可以賦值給block類型變量。它和函數(shù)指針十分相似。

// 函數(shù)指針
void func() {
    
}
void (*funcptr)() = &func;

//block類型變量    
void (^blk)() = ^{
    
    };

在日常開(kāi)發(fā)中,我門會(huì)經(jīng)常使用到block,關(guān)于block 它可以作為

  • block類型變量
  • 屬性
  • 方法參數(shù)
  • 方法返回值
// 1 block作為變量
void (^blk)() = ^{
    
};

// 2 block作為屬性
@property(nonatomic,copy) void(^blk)();

//3 block作為方法參數(shù)
- (void)methodUseBlock:(void (^)())blk {

}

//4 block作為方法返回值
- (void(^)())methodReturnBlock {

    return ^{};
}

平時(shí)為了更方便的使用block,將這種復(fù)雜的記述方式便于使用,可以借助typedef來(lái)使用block

typedef void (^blkType)();

通過(guò)使用typedef,再來(lái)看下實(shí)現(xiàn)之前的block場(chǎng)景

typedef void (^blkType)();

// 1 block作為變量
blkType blk = ^void(){
        
 };
 
// 2 block作為屬性
@property(nonatomic,copy) blkType blk;

//3 block作為方法參數(shù)
- (void)methodUseBlock:(blkType)blk {
    
}

//4 block作為方法返回值
- (blkType)methodReturnBlock {

    return ^{};
}

平時(shí)方便查閱的話,可以到這里看下 http://fuckingblocksyntax.com/

3.截獲的外部變量

保存定義時(shí)的值

在之前介紹block函數(shù)概念的時(shí)候,說(shuō)了block能夠截獲外部變量。而且是在定義block函數(shù)的時(shí)候截獲了外部的變量.對(duì)于局部變量來(lái)說(shuō)在定義的block函數(shù)之后,改變局部變量的值。block內(nèi)部使用的局部變量的值依然是block函數(shù)定義時(shí)的值。如下面的代碼 函數(shù)最后的執(zhí)行結(jié)果依然是 *** a = 10 ***.

void test() {
     int a = 10;
     void(^blk)() = ^void(){
         printf("a = %d",a);
     };
     a = 20;
     blk();
}

在block內(nèi)部改變外部變量

如果block內(nèi)部使用了外部變量,是不允許在block內(nèi)部修改截獲的變量的值的。所以下面的這段代碼就是編譯不過(guò)的。因?yàn)樗淖兞私孬@的外部變量a的值。

    int a = 10;
    void(^blk)() = ^{
        a = 20;
    };

但是下面的這段代碼卻是可以正常編譯的。

    NSMutableArray *tempArray = [NSMutableArray array];
    void(^blk)() = ^{
    
        [tempArray arrayByAddingObject:[[NSObject alloc]init]];
    };

在這段代碼中,blk確實(shí)截獲了外部變量 tempArray這個(gè)數(shù)組,在block內(nèi)部也向數(shù)組里增加了一個(gè)元素。為什么它能編譯通過(guò)呢。其實(shí)block內(nèi)部不允許改變截獲的外部變量應(yīng)該更嚴(yán)格的說(shuō)是不允許改變外部變量的內(nèi)存地址。

    NSMutableArray *tempArray = [NSMutableArray array];
    NSLog(@"原始的內(nèi)存地址 %p",&tempArray);
    [tempArray arrayByAddingObject:[[NSObject alloc]init]];
    NSLog(@"添加元素后的內(nèi)存地址 %p",&tempArray);
    
    打印結(jié)果
    2016-12-18 16:40:19.565 block[8358:803598] 原始的內(nèi)存地址 0x7fff5778cac8
    2016-12-18 16:40:19.565 block[8358:803598] 添加元素后的內(nèi)存地址      0x7fff5778cac8

可以看出內(nèi)存地址并沒(méi)有改變,而如果對(duì)對(duì)象進(jìn)行賦值操作,則會(huì)和之前一樣直接編譯錯(cuò)誤。因?yàn)楦淖兞送獠孔兞康膬?nèi)存地址。

    NSMutableArray *tempArray = [NSMutableArray array];
    void(^blk)() = ^{
    
        tempArray = [NSMutableArray arrayWithObjects:[[NSObject alloc]init], nil];
    };

__block說(shuō)明符

既然在block內(nèi)部無(wú)法直接修改截獲的外部變量的值,而如果我門需要這樣的功能的話,可以通過(guò)對(duì)外部變量加上 _ _ block修飾符則能實(shí)現(xiàn)在block內(nèi)部修改外部變量的值。如下代碼通過(guò)對(duì)外部變量a加上 _ _block 修飾符,就算在block內(nèi)部修改了a的值,也能正常編譯。

    __block int a = 10;
    void(^blk)() = ^{
        a = 20;
    };
    blk();
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容