iOS-Block探究

Blocks是C語言的擴(kuò)充功能,也可以理解為帶有自動變量(局部變量)的匿名函數(shù).C語言中可能用到的變量有自動變量(局部變量),函數(shù)的參數(shù),靜態(tài)變量(靜態(tài)局部變量),靜態(tài)全局變量和全局變量.函數(shù)中能夠傳遞值的變量有靜態(tài)變量(靜態(tài)局部變量),靜態(tài)全局變量和全局變量.

C++和Objective-C使用類可保持變量值且能夠多次持有該變量自身,它會聲明持有成員變量的類,由類生成的實例或?qū)ο蟊3衷摮蓡T變量的值,通過Block可以簡化代碼,實現(xiàn)自動變量的保存.

基礎(chǔ)語法

Block的定義需要定義返回值,Block名字,參數(shù)值,簡單定義如下:

int (^sumBlock)(int a,int b) = ^(int a,int b) {
            return a + b;
        };

調(diào)用測試:

int result = sumBlock(10, 20);
        printf("計算結(jié)果:%d\n",result);

通過使用typedef,函數(shù)定義變得非常容易理解.

typedef int(^SumBlock)(int a, int b);
SumBlock block2 = ^(int a,int b) {
            return a + b;
        };
        
        int result2 = block2(10, 30);

block截獲變量在執(zhí)行的時候中會自動保存下來,之后的修改不會影響block中的輸出結(jié),如果想在block修改變量的值需要加入__block 修飾.

int a = 10;
        int b = 20;
        void (^block3)(void) = ^ {
            printf("a = %d  b = %d\n",a,b);
        };
        
        a = 30;
        b = 40;
        block3();

輸出結(jié)果:

a = 10  b = 20

截獲自動變量的方法沒發(fā)實現(xiàn)對數(shù)組的拷貝,數(shù)組之后的更改是可以反應(yīng)到Block中的.

NSMutableArray *arr = [[NSMutableArray alloc] initWithObjects:@"1",@"2",@"3", nil];
        void (^block4)(void) = ^ {
            NSLog(@"數(shù)組 = %@",arr[1]);
        };
        arr[1] = @"20";
        block4();

輸出結(jié)果:

數(shù)組 = 20

MRC與ARC

估計iOS開發(fā)學(xué)習(xí)中遇到的最多的就是Block了,實際開發(fā)中Block遇到的最多的問題就是循環(huán)引用,先從一份Block測試題目開始吧.下面的五道題目是同樣的Block,大家根據(jù)自己的經(jīng)驗來判斷下面幾個題目在MRC與與ARC下執(zhí)行結(jié)果是否一樣:

題目1:

void exampleA() {
  char a = 'A';
  ^{
    printf("%cn", a);
  }();
}

題目2:

void exampleB_addBlockToArray(NSMutableArray *array) {
  char b = 'B';
  [array addObject:^{
    printf("%cn", b);
  }];
}

void exampleB() {
  NSMutableArray *array = [NSMutableArray array];
  exampleB_addBlockToArray(array);
  void (^block)() = [array objectAtIndex:0];
  block();
}

題目3:

void exampleC_addBlockToArray(NSMutableArray *array) {
  [array addObject:^{
    printf("Cn");
  }];
}

void exampleC() {
  NSMutableArray *array = [NSMutableArray array];
  exampleC_addBlockToArray(array);
  void (^block)() = [array objectAtIndex:0];
  block();
}

題目4:

typedef void (^dBlock)();

dBlock exampleD_getBlock() {
  char d = 'D';
  return ^{
    printf("%cn", d);
  };
}

void exampleD() {
  exampleD_getBlock()();
}

題目5:

typedef void (^eBlock)();

eBlock exampleE_getBlock() {
  char e = 'E';
  void (^block)() = ^{
    printf("%cn", e);
  };
  return block;
}

void exampleE() {
  eBlock block = exampleE_getBlock();
  block();
}

答案:
1.無論在MRC還是在ARC情況都能正確執(zhí)行.
2.ARC情況下執(zhí)行正確,MRC情況下執(zhí)行錯誤.ARC數(shù)組添加的Block的類型是NSMallocBlock類型,MRC則將Block按照NSStackBlock處理.
3.無論是MRC還是ARC添加的Block都按照NSGlobalBlock類型處理,所以都能正確執(zhí)行.
4.只有ARC的情況正確執(zhí)行,MRC中Block在棧中創(chuàng)建的exampled_getblock返回時已經(jīng)無效,而且編譯器會報錯,error: returning block that lives on the local stack.
ARC將在堆中創(chuàng)建一個autorelease的NSMallocBlock類型的Block.
5.只有ARC的情況下正確執(zhí)行,原因同上.

Block 類型

通過測試題目我們發(fā)現(xiàn)了Block有三種類NSConcreteGlobalBlock,NSConcreteStackBlock和NSConcreteMallocBlock,事實上Block還有三種適用于GC的類型NSConcreteWeakBlock,NSConcreteAutoBlock和NSConcreteFinalizingBlock.

1.全局Block:如果block沒有訪問外界的任何變量或者對象,那么block就是一個全局block:

2.MRC中如果Block訪問了外界變量就會變成棧block.棧上的Block,如果其所屬的變量作用域結(jié)束,該Block就被釋放.

3.ARC為了解決棧Block釋放出現(xiàn)的問題,會通過編譯器默認(rèn)的會將棧Block進(jìn)行copy然后變成堆Block,通過引用計數(shù)管理Block,延長其生命周期.

以下是三個不同類型的Block展現(xiàn)形式:

typedef int (^SumBlock)(int a,int b);

@interface ViewController ()

@property (assign, nonatomic) SumBlock stackBlock;

@end
void (^globalBlock)() = ^() {
        NSLog(@"FlyElephant---全局Block");
    };
    
    NSLog(@"FlyElephant---%@",globalBlock);
    
    __weak typeof(self) weakSelf = self;
    self.stackBlock = ^int(int a, int b) {
        NSLog(@"FlyElephant---%@",weakSelf.stackBlock);
        return a + b;
    };
    
    NSInteger test = self.stackBlock(10,10);
    NSLog(@"FlyElephant---%ld---%@",test,self.stackBlock);
    
    NSInteger num = 27;
    
    void (^mallocBlock)() = ^() {
        NSLog(@"FlyElephant---棧Block--%ld",num);
    };
    NSLog(@"FlyElephant---%@",mallocBlock);
FlyElephant.png

友情提示:ARC項目中Block使用copy修飾,千萬不要用assign修飾,本文為演示,特意用assign對Block進(jìn)行修飾.

Block 原理

Block是對包含上下文變量的函數(shù)的封裝,是Objective-C語言對閉包的另外一種形式的封裝.Block同時也是一個對象,不過Block內(nèi)部的構(gòu)造與一般的對象差別很多.

block-struct.jpg

Block的內(nèi)存布局是由六個部分組成:
1.isa 指針:所有對象都有該指針,指向Class對象.
2.flags:用于按 bit 位表示一些 block 的附加信息.
3.reserved:保留變量.
4.invoke:最重要的變量,函數(shù)指針,指向具體的 block 實現(xiàn)的函數(shù)調(diào)用地址,至少要接收一個void *型的參數(shù).
5.descriptor:指向結(jié)構(gòu)體的指針,表示該 block 的附加描述信息,主要是 size 大小,以及 copy 和 dispose 函數(shù)的指針.
6.variables:捕獲(capture)過來的變量,block 能夠訪問它外部的局部變量,就是因為將這些變量(或變量的地址)復(fù)制到了結(jié)構(gòu)體中.

參考資料:
Objective-C Blocks Quiz
A look inside blocks: Episode 3 (Block_copy)

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

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

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