iOS--Block

weixin_33895657發表於2016-09-11

先定個小目標,例如整理一篇關於 block 的筆記

  • 先用 OC 寫一段最簡單的 block 程式碼:

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            void (^myBlock)(void) = ^{
                NSLog(@"hello world");
            } ;
            myBlock() ;
        }
        return 0;
    }
    
  • 使用 clang -rewrite-objc 檢視 block 的原始碼:

```objc
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
     
        void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA)) ;
        
        ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock) ;
    }
    return 0;
}
```
  • 為了提高上述程式碼的可讀性,將型別轉化的程式碼去掉,變成如下程式碼:

    int main(){
        (*myBlock) = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA) ;
        myBlock -> FuncPtr ;
    }
    
    
  • 現在讓我們來看下這兩句程式碼的作用到底是什麼

    首先我們發現,我們原始碼中程式碼塊裡面的程式碼 “不見了”。在轉化後的程式碼中,我們沒有找到程式碼塊裡面的。

    ^{
        NSLog(@"hello world");
     } ;
    
  • 我們先看 __mian_block_func_0 這個引數到底是什麼。檢視 main.cpp 程式碼可以發現原來它是個方法,引數為 self (程式碼塊):

    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
            NSLog((NSString*)&__NSConstantStringImpl__var_folders_v4_lj0qxzgx4bn62svsmmv5gk240000gn_T_main_cc2878_mi_0);
    }
    
  • 由此我們可以知道我們程式碼塊的內容都儲存在 __mian_block_func_0 這個方法裡面。然後作為引數傳遞到 __main_block_impl_0 這個方法裡面。那麼 __main_block_impl_0 這個方法又有什麼作用呢?檢視 mian.cpp 程式碼可以發現:

    struct __main_block_impl_0 {
      struct __block_impl impl;
      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 __block_impl impl

    該程式碼申明瞭一個 block 。__block_impl 的具體內容如下所示,可以看出 block 其實就是個結構體:

    struct __block_impl {
      void *isa;        //  指向物件該 block 的型別
      int Flags;        //  該 block 的一些其他資訊
      int Reserved;     //  保留區
      void *FuncPtr;    //  該 block 指向的記憶體地址
    };
    

    struct __main_block_desc_0* Desc;

    該程式碼申明瞭一個關於該 block 的一些資訊的方法。其實現如下所示:

    static struct __main_block_desc_0 {
      size_t reserved;                  //  保留區的大小
      size_t Block_size;                //  block 的大小
    } __main_block_desc_0_DATA = {      // 快速初始化方法
      0, 
      sizeof(struct __main_block_impl_0)
    };
    

    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0)

    該方法為 __main_block_impl_0 的建構函式,主要為 __block_impl 型別建立的 block 賦值。

    • fp 指向前面所述的__mian_block_func_0 的地址
    • desc 指向掐面所述的 __main_block_desc_0
    • flag...
  1. 綜上所述,我們應該能夠明白下面程式碼的作用了,就是將建立一個 block,初始化該結構體,在建構函式中傳入自身(self) __main_block_func_0 結構體和基本資訊。

    (*myBlock) = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA) ;
    
  2. 得到新建立的 block 後,當我們呼叫原本程式碼的 block時候,就相當於呼叫:

    myBlock -> FunPtr
    

參考資料