天天看點

Block底層實作原理

Block

int main(int argc, const char * argv[]) {
@autoreleasepool {
    TYBlock block = ^(){
        NSLog(@"hello block");
    };
    block();
    
    NSLog(@"Hello, World!");
    }
    return 0;
}
           

clang -rewrite-objc main.m

// block 的結構體
struct __block_impl {  // block 的結構體
  void *isa;           // isa 指針 類比對象
  int Flags;           //
  int Reserved;
  void *FuncPtr;     // 函數指針, 指向block自身實作
};

// block的實作, 實作入口
struct __main_block_impl_0 {
  struct __block_impl impl;   // block類型成員變量 isa  Flags Reserved *FuncPtr;
  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;
  }
};

// block 執行的函數  __main_block_func_0
static void __main_block_func_0(struct __main_block_impl_0 *__cself) { // block 要執行的靜态方法
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_fm_w1qcdkwj68v18gnz_z89g02c0000gn_T_main_2dcb54_mi_0);
}

// block 描述資訊結構體  __main_block_desc_0
static struct __main_block_desc_0 { //        __main_block_impl_0 的描述結構體
  size_t reserved;
  size_t Block_size; // 結構體大小
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};

// main函數
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
    
    //
    /**
     block 實作
     @param 1 的靜态方法
     @param 2 __main_block_desc_0 __main_block_impl_0的描述結構體
     */
    TYBlock block = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
    // block 調用
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);

    NSLog((NSString *)&__NSConstantStringImpl__var_folders_fm_w1qcdkwj68v18gnz_z89g02c0000gn_T_main_2dcb54_mi_1);
}
return 0;
}
           

持有局部變量Block

int a = 10;
    TYBlock block = ^(){
        NSLog(@"hello block %d", a);
    };
    block();
           
  • cpp檔案
    // block的實作入口多了一個 外部變量 int a
    struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    int a; // 外部變量 block通路的外部變量
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int flags=0) : a(_a) { // 在構造函數之前初始化變量a
      impl.isa = &_NSConcreteStackBlock;
          impl.Flags = flags;
          impl.FuncPtr = fp;
          Desc = desc;
       }
    };
    
    // 實作函數
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    int a = __cself->a; // 多了一個外部變量a。__main_block_impl_0中的a指派給函數中的臨時變量a
    
              NSLog((NSString *)&__NSConstantStringImpl__var_folders_fm_w1qcdkwj68v18gnz_z89g02c0000gn_T_main_df3697_mii_0, a);
          }
          
    // block的表述類
    static struct __main_block_desc_0 {
    size_t reserved;
    size_t Block_size;
    } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
    
    // main函數
    int a = 10;
      TYBlock block = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a));
      ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
      NSLog((NSString *)&__NSConstantStringImpl__var_folders_fm_w1qcdkwj68v18gnz_z89g02c0000gn_T_main_df3697_mii_1, block);
               
    block入口函數 __main_block_impl_0中多了一個成員變量(block通路的局部變量)__main_block_func_0 函數調用時。參數__main_block_impl_0中的成員變量a指派給函數中的臨時變量a。是以這種方式捕獲的外部變量不可修改并且外部修改後也不能随之更改。

捕獲__block 修飾局部變量

__block int a = 10;
    TYBlock block = ^(){
        NSLog(@"block内====%d", a); // 20
        a = 100;
    };
    a = 20;
    block();
    NSLog(@"block外=====%d", a); // 100
           
  • cpp
    // 封裝__block修飾的外部變量
      struct __Block_byref_a_0 {
          void *__isa;                     // 對象指針
          __Block_byref_a_0 *__forwarding; // 堆中的位址 也就是指向自己的指針
          int __flags; // 标志位
          int __size;  // 結構體大小
          int a;       // 外部變量
      };
    
      struct __main_block_impl_0 {
          struct __block_impl impl;
          struct __main_block_desc_0* Desc;
          __Block_byref_a_0 *a; //  通路的外部變量a 變成__Block_byref_a_0類型的結構體指針。__block修飾的變量通過指針傳遞進來
          __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc,     __Block_byref_a_0 *_a, int flags=0) : a(_a->__forwarding) {
          impl.isa = &_NSConcreteStackBlock;
          impl.Flags = flags;
          impl.FuncPtr = fp;
          Desc = desc;
          }
      };
      
      static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
          __Block_byref_a_0 *a = __cself->a; // bound by ref
          // a->__forwarding->a) // 堆中的a
          NSLog((NSString *)&__NSConstantStringImpl__var_folders_fm_w1qcdkwj68v18gnz_z89g02c0000gn_T_main_537bc1_mii_0, (a->__forwarding->a));
      }
      
      // 添加了copy和dispose函數為了拷貝和析構__Block_byref_a_0結構體,對改結構體進行記憶體管理使用
      static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
          // _Block_object_assign 函數:當block從棧拷貝到堆時,調用此函數。
          _Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);
      }
      // 當block從堆釋放時調用 __main_block_dispose_0函數
      static void __main_block_dispose_0(struct __main_block_impl_0*src) {
          _Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);
      }
      
      // 當block從堆内釋放的時候調用 __main_block_impl_0 釋放記憶體
      static struct __main_block_desc_0 {
          size_t reserved;
          size_t Block_size;
          void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
          void (*dispose)(struct __main_block_impl_0*);
      } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
               

block 通路的__block修飾的外部變量就是一個結構體 __Block_byref_a_0

// 封裝__block修飾的外部變量
    struct __Block_byref_a_0 {
        void *__isa;                     // 對象指針
        __Block_byref_a_0 *__forwarding; // 堆中的位址 也就是指向自己的指針
        int __flags; // 标志位
        int __size;  // 結構體大小
        int a;       // 外部變量
    };
           

int a 是外部變量名。 __Block_byref_a_0 *__forwarding 這個是指向自己的指針

為了對__Block_byref_a_0 添加了copy和dispose函數為了拷貝和析構__Block_byref_a_0結構體。

__main_block_impl_0中增加了__Block_byref_a_0類型的成員變量指針。是以__block的變量可以修改,是因為指針傳遞。是以block内部修改了值,外部也會改變。

struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    __Block_byref_a_0 *a; //  通路的外部變量a 變成__Block_byref_a_0類型的結構體指針。__block修飾的變量通過指針傳遞進來
    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc,     __Block_byref_a_0 *_a, int flags=0) : a(_a->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
    }
};
           

在調用函數__main_block_func_0 時,a是

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    __Block_byref_a_0 *a = __cself->a; // 指針棧中 __Block_byref_a_0
    // a->__forwarding->a) // 堆中的a  // 堆中的forwarding 的成員變量a
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_fm_w1qcdkwj68v18gnz_z89g02c0000gn_T_main_537bc1_mii_0, (a->__forwarding->a));
}
           

用__block修改後,testNum3變量轉換為__Block_byref_num_0 的結構體。

// man函數 __forwarding 指向自身的指針
__attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 10};
TYBlock block = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344));
(a.__forwarding->a) = 20;
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_fm_w1qcdkwj68v18gnz_z89g02c0000gn_T_main_adc47b_mii_1, (a.__forwarding->a));
           

a變量用__Block_byref_a_0包裹,變成__Block_byref_a_0結構體

無論是棧上的a變量用__Block_byref_a_0還是堆中的a變量用__Block_byref_a_0。修改時都是通過變成__Block_byref_a_0 的 __forwarding中的數值。是以修改的都是堆中的__Block_byref_a_0中的a

注:static修飾的局部靜态變量,沒有使用__Block_byref_a_0結構體包裝,直接傳遞指針。__Block_byref_a_0的目的是把棧上的變量拷貝到堆中,延長生命周期。接獲成員變量的block是堆block也印證這一點。