iOS 深入理解Block

## Block拷貝

 

block的屬性需要使用copy修飾,如果block一旦沒有copy操作就不會在堆上,無法對block生命週期進行控制。

使用注意:循環引用的問題。

block在修改NSMutableArray,不需要添加__block,因爲修改內容也是對數組的使用,只有對對象賦值的時候才需要__block。

 

## __Block

 

當使用__block修飾符時,基本數據類型 a 被轉換成了__Block_byref_a_0結構體。__Block_byref_a_0結構體中帶有 isa指針,說明它也是一個對象。

當block修改變量時,會調用下面代碼:

__block修飾變量用結構體成員變量__forwarding可以實現無論 __block變量配置在棧上還是堆上都能夠正確的訪問 __block變量。

__forwarding指針始終指向自己。

當__block int a在棧中的時候,__forwarding指向的是棧中的自己。

當 i 拷貝到堆中時候,__forwarding指向的是堆中的自己。

 

## 鏈式編程

 

### ReactiveCocoa

 

### Masonry

 

### RxSwift

 

### SnapKit

 

## 類型

 

注意:在 ARC 開啓的情況下,將只會有 NSConcreteGlobalBlock 和 NSConcreteMallocBlock 類型的 block。因爲Block從棧拷貝到堆中。

 

### NSConcreteGlobalBlock

struct __Block_byref_a_0 {

 void *__isa;

__Block_byref_a_0 *__forwarding;

 int __flags;

 int __size;

 int a;

};

struct __Block_byref_obj_1 {

 void *__isa;

__Block_byref_obj_1 *__forwarding;

 int __flags;

 int __size;

 void (*__Block_byref_id_object_copy)(void*, void*);

 void (*__Block_byref_id_object_dispose)(void*);

 NSObject *obj;

};

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

 __Block_byref_a_0 *a = __cself->a; // bound by ref

 __Block_byref_obj_1 *obj = __cself->obj; // bound by ref



      (a->__forwarding->a)++;

      (obj->__forwarding->obj) = ((NSMutableArray * _Nonnull (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSMutableArray"), sel_registerName("array"));



      printf("截獲後--:%d", (a->__forwarding->a));

    }

NSGlobalBlock是位於全局區的block,它是設置在程序的數據區域(.data區)中。

以下兩種情況下,block爲NSConcreteGlobalBlock

a.記述全局變量的地方有block語法時。

b.block語法的表達式中不使用任何外部變量時。

 

- 全局區block底層原理


  struct __block_impl {

   void *isa;//指向所屬類的指針,也就是block的類型

   int Flags;//標誌變量,在實現block的內部操作時會用到

   int Reserved;//保留變量

   void *FuncPtr;//block執行時調用的函數指針

  };

  struct __globalBlock_block_impl_0 {

   struct __block_impl impl;

   struct __globalBlock_block_desc_0* Desc;

   __globalBlock_block_impl_0(void *fp, struct __globalBlock_block_desc_0 *desc, int flags=0) {

    impl.isa = &_NSConcreteGlobalBlock;

    impl.Flags = flags;

    impl.FuncPtr = fp;

    Desc = desc;

   }

  };

  static void __globalBlock_block_func_0(struct __globalBlock_block_impl_0 *__cself) {

  

    static int g = 10;

    printf("%d",g);

  }

  

  static struct __globalBlock_block_desc_0 {

   size_t reserved;

   size_t Block_size;

  } __globalBlock_block_desc_0_DATA = { 0, sizeof(struct __globalBlock_block_impl_0)};

  static __globalBlock_block_impl_0 __global_globalBlock_block_impl_0((void *)__globalBlock_block_func_0, &__globalBlock_block_desc_0_DATA);

  void (*globalBlock)() = ((void (*)())&__global_globalBlock_block_impl_0);

  int main(int argc, const char * argv[]) {

    return 0;

  }

  static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };

### NSConcreteStackBlock

 

NSStackBlock是位於棧區,超出變量作用域,棧上的Block以及 __block變量都被銷燬。

 

- 棧區block底層原理


  struct __block_impl {

   void *isa;

   int Flags;

   int Reserved;

   void *FuncPtr;

  };

  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;//這個是C++的結構體 該函數是C++結構體的初始化函數

  

    impl.Flags = flags;

    impl.FuncPtr = fp;

    Desc = desc;

   }

  };

  static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

  

  

    }

  

  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)};

  int main(int argc, const char * argv[]) {

  

    printf("Hello, World!\n");

    int a = 10;

    printf("%d", a);

    void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));

    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);

    return 0;

  }

### NSConcreteMallocBlock

 

NSMallocBlock是位於堆區,在變量作用域結束時不受影響。保存在堆中的 block,當引用計數爲 0 時會被銷燬。

堆中的block無法直接創建,其需要由_NSConcreteStackBlock類型的block拷貝而來(也就是說block需要執行copy之後才能存放到堆中)。由於block的拷貝最終都會調用_Block_copy_internal函數。

 

- 堆區block底層原理


  __block int a = 10;

      printf("截獲前:%d", a);

      void (^block)(void) = [^{

        a++;

        printf("截獲後--:%d", a);

      } copy];

      a++;

      printf("截獲後:%d", a);

      block();

  

  //這樣的話block的isa就是NSConcreteMallocBlock

  typedef void (*blk_t)(void);

  

  

  struct __getBlockArray0_block_impl_0 {

   struct __block_impl impl;

   struct __getBlockArray0_block_desc_0* Desc;

   int val;

   __getBlockArray0_block_impl_0(void *fp, struct __getBlockArray0_block_desc_0 *desc, int _val, int flags=0) : val(_val) {

    impl.isa = &_NSConcreteStackBlock;

    impl.Flags = flags;

    impl.FuncPtr = fp;

    Desc = desc;

   }

  };

  static void __getBlockArray0_block_func_0(struct __getBlockArray0_block_impl_0 *__cself) {

   int val = __cself->val; // bound by copy

  NSLog((NSString *)&__NSConstantStringImpl__var_folders_77__vw6s40s54v9p9d9zb4f_3br0000gn_T_main_c1dee6_mi_0,val);}

  

  static struct __getBlockArray0_block_desc_0 {

   size_t reserved;

   size_t Block_size;

  } __getBlockArray0_block_desc_0_DATA = { 0, sizeof(struct __getBlockArray0_block_impl_0)};

  

  struct __getBlockArray0_block_impl_1 {

   struct __block_impl impl;

   struct __getBlockArray0_block_desc_1* Desc;

   int val;

   __getBlockArray0_block_impl_1(void *fp, struct __getBlockArray0_block_desc_1 *desc, int _val, int flags=0) : val(_val) {

    impl.isa = &_NSConcreteStackBlock;

    impl.Flags = flags;

    impl.FuncPtr = fp;

    Desc = desc;

   }

  };

  static void __getBlockArray0_block_func_1(struct __getBlockArray0_block_impl_1 *__cself) {

   int val = __cself->val; // bound by copy

  NSLog((NSString *)&__NSConstantStringImpl__var_folders_77__vw6s40s54v9p9d9zb4f_3br0000gn_T_main_c1dee6_mi_1,val);}

  

  static struct __getBlockArray0_block_desc_1 {

   size_t reserved;

   size_t Block_size;

  } __getBlockArray0_block_desc_1_DATA = { 0, sizeof(struct __getBlockArray0_block_impl_1)};

  NSArray *getBlockArray0() {

    int val =10;

    return ((NSArray * _Nonnull (*)(id, SEL, ObjectType _Nonnull, ...))(void *)objc_msgSend)((id)objc_getClass("NSArray"), sel_registerName("arrayWithObjects:"), (id _Nonnull)((void (*)())&__getBlockArray0_block_impl_0((void *)__getBlockArray0_block_func_0, &__getBlockArray0_block_desc_0_DATA, val)), ((void (*)())&__getBlockArray0_block_impl_1((void *)__getBlockArray0_block_func_1, &__getBlockArray0_block_desc_1_DATA, val)), __null);

  }

  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; // by ref

   __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)++;

        printf("截獲後--:%d", (a->__forwarding->a));

      }

  static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);}

  

  static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);}

  

  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};

  int main(int argc, char * argv[]) {

  

    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

  

  

      __attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 10};

      printf("截獲前:%d", (a.__forwarding->a));

      void (*block)(void) = (void (*)())((id (*)(id, SEL))(void *)objc_msgSend)((id)((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344)), sel_registerName("copy"));

      (a.__forwarding->a)++;

      printf("截獲後:%d", (a.__forwarding->a));

      ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);

  

    }

    return 0;

  

  }

## Block的本質

 

block本質上也是一個OC對象,它內部也有個isa指針

block是封裝了函數調用以及函數調用環境的OC對象

 

## 引用循環

 

產生引用循環的原因,是在堆上的對象與堆上的對象互相引用造成的環,就會引起循環引用。

在ARC下,只要將block賦值給一個變量,那麼這個block就將被拷貝到堆上,這是編譯器的優化。所以,這種情況下,block被強引用,block又對捕獲的對象強引用,我們就必須使用__weak來打破循環。但是有時候可能導致在block的過程被釋放了,我們就需要使用__strong對block外的__weak對象再次強引用,既能夠防止引用循環,又能夠保證代碼的正確執行。

可以傳入一個局部變量(即self),這樣就不會導致引用循環了。

可以__block解決(必要時調用block)。

 

## 截獲自動變量

 

這個其實也是block底層原理

利用__block

有三種情況不用__block也可以截獲變量的值:

靜態全局變量

全局變量

靜態變量

 

*XMind: ZEN - Trial Version*

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章