Block深入學習,授人以漁。—— Block與各種變數

獨樂樂發表於2018-03-08

前言

Block的文章看了很多,但是都不得要領。最近看了霜神的文章(https://juejin.im/post/57ccab0ba22b9d006ba26de1),然後又重讀了一遍《Objective-C高階程式設計》,根據文章和書中的內容,用clang把OC轉成C,然後才懂了很多原來似是而非的問題。

歡迎討論,更歡迎大佬指點。

怎麼才能真正瞭解Block

1.知道clang的幾個指令

2.有C的基礎

3.親自動手去試

需要用到的clang指令與操作 我是先轉c檔案,看的差不多了,然後轉oc檔案。循序漸進。

0.cd 進入目標檔案的資料夾 clang -rewrite-objc xxxx.c 在資料夾下找.cpp檔案 建議先用c檔案來嘗試。

1.cd 進入目標檔案的資料夾clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk xxx.m xxx是OC檔案的名。

先建立一個c檔案 寫如下程式碼

int test(void){

     void (^studyBlock)(void) = ^{

     printf("這是 studyBlock");
};

studyBlock();
}
複製程式碼

**通過clang -rewrite-objc xxxx.c 得到了一個接近600行的cpp檔案。很多程式碼是與Block無關的。相關程式碼如下:請注意區分大小寫。

//Block 結構體 相當於Foundation框架中的NSObject物件,因為有isa指標,所以Block也可看做物件的一種。;
struct __block_impl {
 void *isa;
  int Flags;
  int Reserved;
 void *FuncPtr;
};

//這是我們宣告的block( void(^block)(void) ),這是一個block宣告,轉換完之後程式碼有這麼多。。
struct __test_block_impl_0 {
    struct __block_impl impl;
    struct __test_block_desc_0* Desc;
    __test_block_impl_0(void *fp, struct __test_block_desc_0 *desc, int flags=0) {
  impl.isa = &_NSConcreteStackBlock;
  impl.Flags = flags;
  impl.FuncPtr = fp;
  Desc = desc;
  }
};

//studyBlock體中的內容^{ printf("這是 studyBlock"); };
static void __test_block_func_0(struct __test_block_impl_0 *__cself) {

    printf("這是 studyBlock");
}

//每個Block轉化之後都有這樣的結構體,用於記錄版本和大小
static struct __test_block_desc_0 {
    size_t reserved;
    size_t Block_size;
} __test_block_desc_0_DATA = { 0, sizeof(struct __test_block_impl_0)};

int test(void){
    //studyBlock呼叫 studyBlock();
     void (*studyBlock)(void) = ((void (*)())&__test_block_impl_0((void *)__test_block_func_0, &__test_block_desc_0_DATA));

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

  return 0;
}
複製程式碼

好了現在知道了 Block的真面目了。下面加入一些變數。程式碼如下。

//加入了全域性變數 全域性靜態變數 區域性靜態變數 __block變數 區域性變數(自動變數)
int globelVal = 0;

static int staticGlobelVal = 1;

int test(void){

     static int staticVal = 2;

     __block int blockVal = 3;

     int val = 4;

     void (^studyBlock)(void) = ^{
    
        printf("%d===%d===%d===%d===%d",globelVal,staticGlobelVal,staticVal,blockVal,val);
   
     };

     studyBlock();

     return 0;
}
複製程式碼

程式碼很少 也很好理解,下面我們轉換一下

 struct __block_impl {
     void *isa;
     int Flags;
     int Reserved;
     void *FuncPtr;
 };

 int globelVal = 0;

 static int staticGlobelVal = 1;
    
 //__block型別的變數
 //結構體中的 isa指標,指向原變數isa,用於儲存變數型別。
 //__Block_byref_blockVal_0 。__Block_byref_blockVal_0結構中的__Block_byref_blockVal_0。為什麼要有這樣的結構呢。是因為Block有可能從棧中複製到堆中。
 //當Block從棧中複製到堆中,結構體中的__forwarding指向棧中的__Block_byref_blockVal_0結構體。然後通過__forwarding->blockVal來操作變數的值。
 //如果沒有複製到棧中的操作。__forwarding指向棧中的__Block_byref_blockVal_0結構體。
 struct __Block_byref_blockVal_0 {
     void *__isa;
     __Block_byref_blockVal_0 *__forwarding;
     int __flags;
     int __size;
     int blockVal;
 };

 struct __test_block_impl_0 {
     struct __block_impl impl;
     struct __test_block_desc_0* Desc;
     //我們可以看到靜態區域性變數和區域性變數的區別了。
     //靜態區域性變數被引入studyBlock結構體的時候,是通過指標的方式引入的。通過指標可以修改記憶體中該變數的值。那麼再看區域性變數,只是一個值被引入了,所以不能修改區域性變數的值。
     //__Block_byref_blockVal_0 *blockVal;這個是__blockVal;
     //被引入studyBlock結構體之後,變成了一個__Block_byref_blockVal_0型的結構體變數。
     int *staticVal;
     int val;
     __Block_byref_blockVal_0 *blockVal; // by ref
     __test_block_impl_0(void *fp, struct __test_block_desc_0 *desc, int *_staticVal, int _val, __Block_byref_blockVal_0 *_blockVal, int flags=0) : staticVal(_staticVal), val(_val), blockVal(_blockVal->__forwarding) {
     impl.isa = &_NSConcreteStackBlock;
     mpl.Flags = flags;
     impl.FuncPtr = fp;
     Desc = desc;
    }
};

 static void __test_block_func_0(struct __test_block_impl_0 *__cself) {
     __Block_byref_blockVal_0 *blockVal = __cself->blockVal; // bound by ref
     int *staticVal = __cself->staticVal; // bound by copy
     int val = __cself->val; // bound by copy
     printf("%d===%d===%d===%d===%d",globelVal,staticGlobelVal,(*staticVal),(blockVal->__forwarding->blockVal),val);
}

 static void __test_block_copy_0(struct __test_block_impl_0*dst, struct __test_block_impl_0*src) {_Block_object_assign((void*)&dst->blockVal, (void*)src->blockVal, 8/*BLOCK_FIELD_IS_BYREF*/);}

 static void __test_block_dispose_0(struct __test_block_impl_0*src) {_Block_object_dispose((void*)src->blockVal, 8/*BLOCK_FIELD_IS_BYREF*/);}

 static struct __test_block_desc_0 {
     size_t reserved;
     size_t Block_size;
     void (*copy)(struct __test_block_impl_0*, struct __test_block_impl_0*);
     void (*dispose)(struct __test_block_impl_0*);
 } __test_block_desc_0_DATA = { 0, sizeof(struct __test_block_impl_0), __test_block_copy_0, __test_block_dispose_0};
 
 
 int test(void){

     static int staticVal = 2;

     __attribute__((__blocks__(byref))) __Block_byref_blockVal_0 blockVal = {(void*)0,(__Block_byref_blockVal_0 *)&blockVal, 0, sizeof(__Block_byref_blockVal_0), 3};

     int val = 4;

     void (*studyBlock)(void) = ((void (*)())&__test_block_impl_0((void *)__test_block_func_0, &__test_block_desc_0_DATA, &staticVal, val, (__Block_byref_blockVal_0 *)&blockVal, 570425344));

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

return 0;

}
複製程式碼

補充 Block的isa指標分為三種型別。_NSConcreteStackBlock棧。_NSConcreteGlobalBlock資料區。_NSConcreteMallocBlock堆。

課後題

1.為什麼普通變數不能再Block中修改。

2.Block分為幾種型別

3.__block型別為什麼能夠在Block中修改值。

下一節 探究weakSelf,strongSelf。

相關文章