block是如何實現的?

先看一段代碼,打印結果是什么?如果將int val =3改為__block int val =3呢?為什么?

int val=3;
void(^block)()=^{
  NSLog(@"%d",val);
 };
  val=5;
  block(); 

block是什么?

很多教程資料上的解釋是“帶有自動變量值的匿名函數”。但這種解釋不利于理解。其實對于一個block來說:它更像一個微型的程序。
我們知道程序就是數據加上算法,顯然,block有著自己的數據和算法??梢钥吹健T谶@個簡單的例子中,block的數據就是int類型變量val,它的算法就是簡單的NSLog方法。對于一般的block來說,他的數據就是傳入的參數和定義這個block時截獲的變量。而它的算法就是我們往里面寫的那些方法,函數調用等。
認為block像是一個微型程序的另一個原因是block對象可以由程序員選擇在什么時候調用,比如,我可以自己選擇時機執行這個block,或者在另一個類里執行這個block。
Block是一個Objective-C的對象。

block是如何實現的?

block的定義和調用是分離的,通過clang編譯器,可以看到block和其他OC對象一樣,都是被編譯成C語言里普通的struct結構體來實現的。
源碼:

int main(){
   void(^block)(void) = ^{printf("Block\n");};
   block();
   return0; 
}

編譯后:

struct __block_impl {
      void *isa;
      int Flags;
      int Reserved;
      void*FuncPtr; 
};
struct __main_block_impl_0 {
    struct __block_impl imply;
    struct __main_block_desc_0 *Desc; 

   __main_block_impl_0(void*fp,struct __main_block_desc_0 *desc,int flags=0){
   impl.isa = &_NSConcreteStackBlock; 
   impl.Flags = flags; 
   impl.FuncPtr = fp; Desc = desc; 
} 
};
struct void __main_block_func_0(struct __main_block_impl_0 *__cself){
printf("Block\n"); 
}
static struct__main_block_desc_0{
  unsigned long reserved;
  unsigned long Block_size; 
} __main_block_desc_0_DATA = {
   0,
  sizeof(struct __main_block_impl_0) 
};

代碼非常長,但是并不復雜,一共四個結構體,顯然一個block對象被編譯為了一個____main_block_impl_0__類型的結構體。這個結構體由兩個成員變量結構體和一個構造函數組成。兩個結構體分別是____block_impl__和main_block_desc_0類型的。其中____block_impl__結構體中有一個函數指針,指針將指向____mian_block_func_0__類型的結構體。關系圖如下:

31EBA649-DDE8-4E7C-94D4-8222D7779F7B.png

block在定義的時候:
//調用____main_block_impl_0結構體的構造函數

struct __main_block_impl_0 tmp = __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);
struct __main_block_impl_0 *blk = &tmp;

block在調用的時候:

(*blk->impl.FuncPtr)(bulk);

之前說到,block有自己的數據和算法。顯然算法就是放在__main_block_func_0結構體中的。那么數據在哪里呢?這個問題比較復雜,先看一下文章最初的demo編譯成什么樣,為簡化代碼,這里只貼出需要修改的部分。

  struct  __block_impl imply;
  struct  __main_block_desc_0 *Desc;
  int val; 
  __main_block_impl_0(void *fp,struct __main_block_desc_0 *desc,int_val,int flags=0     ) :val(_val){ 
     impl.isa = &_NSConcreteStackBlock; 
     impl.Flags = flags; 
     impl.FuncPtr = fp; 
      Desc = desc; 
   }
 };
struct void __main_block_func_0(struct__main  _block_impl_0 *__cself){
  int val= __cself->val; 
  printf("val = %d",val); 
}

可以看到,當block需要截獲自動變量的時候,首先會在____mian_block_impl_0__結構體中增加一個成員變量并在結構體的構造函數中對變量賦值,以上這些對應著block對象的定義。
在block被執行的時候,把____mian_block_impl_0__結構體,也就是blcok對象當做參數傳入__mian_block_func_0結構體中,取出其中val的值,進行接下來的操作。

為什么block中不能修改變量的值?

通過把block拆成這四個結構體,系統’完美’的實現了一個block,使得它可以結構自動變量,也可以像一個微型程序一樣在任何時刻都可以被調用。但是,block還存在一個致命的不足:
注意到之前的____mian_block_func_0__結構體,里面有printf方法,用到了val,但是這個block和最初block截獲的block,除了數值一樣,在也沒有一樣的地方了。參見這句代碼:
int val= ____cself->val;__
當然這并沒什么影響,甚至還有好處,因為 int val變量定義在棧上,在block調用時已經被銷毀,但是我們還可以正常訪問這個變量。但是試想一下,如果我希望在block中修改變量的值,那么收到影響的事int val 而非cself->val,事實上即使是__cself->val,也只是截獲的自動變量的副本,要想修改block定義之外的自動變量,是不可能的事情。
既然無法實現修改截獲的自動變量,那么編譯器干脆就禁止程序員這么做了。

__block修飾符是如何做到修改變量的?

__block int val =3;//修改后的代碼
編譯后:

struct __Block_byref_val_0 { 
  void *__isa;
  __Block_byref_val_0 *forwarding;
  int __flags; 
  int __size;
  int val; 
};
struct __main_block_impl_0 {
  struct __block_impl imply;
  struct __main_block_desc_0 *Desc; 
  __Block_byref_val_0 *val; 
  __main_block_impl_0(void *fp,struct__main_block_desc_0 *desc,__Block_byref_val_0 *_val,intflags=0) :val(_val->__forwrding){ 
  impl.isa = &_NSConcreteStackBlock; 
  impl.Flags = flags; 
  impl.FuncPtr = fp; 
  Desc = desc; 
  } 
};
struct void __main_block_func_0(struct__main_block_impl_0 *__cself){
   __Block_byref_val_0 *val= __cself->val; 
  printf("val = %d",val->__forwarding->val); }

改動并不大,簡單來說,只是把val封裝在了一個結構體中而已,五個結構體之間關系如下:


1BABC288-87A7-474A-B81C-D6C6FD7AA7D4.png

關鍵在于____mian_block_impl_0__結構體中的這一行:
Block_byref_val_0 *val;
由于main_block_impl_0__結構體中現在保存了一個指針變量,所以任何對這個指針的操作,是可以影響到原來的變量的。

進一步,我們考慮截獲的自動變量是Objective-C的對象的情況。在開啟ARC的情況下,將會強引用這個對象一次。這也保證了原對象不被銷毀,但與此同時,也會導致循環引用問題。


希望對你有所幫助!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容