前言
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中修改值。