上一篇 講的是block靜態變量的特性,這里我們來看一下_block變量。引用官方:
You can specify that an imported variable be mutable—that is, read-write— by applying the__block
storage type modifier.__block
storage is similar to, but mutually exclusive of, theregister
,auto
, andstatic
storage types for local variables.
通過指定__block存儲類型修飾符,可以讀寫。__block存儲是類似的,但相互排斥的,寄存器,自動變量,和局部變量和靜態變量類型(后面這句不太理解)。
我們來寫個例子,用__block修飾局部變量(對于全局變量是無效的,沒有任何作用,全局變量成員變量本身就可以在block內部修改,詳細看這一篇 ):
void main1(){ __block char *_para1="a"; PRintf("init _para1:%s,%p,%p/n",_para1,_para1,&_para1); void(^testBlock)(void)=^{ printf("exute _para1:%s,%p,%p/n",_para1,_para1,&_para1); }; _para1="b"; printf("before _para1:%s,%p,%p/n",_para1,_para1,&_para1); testBlock(); printf("after _para1:%s,%p,%p/n",_para1,_para1,&_para1);}
執行后輸出如下:
init _para1:a,0x47f4,0xbfffc9c0
before _para1:b,0x4829,0x8da4580
exute _para1:b,0x4829,0x8da4580
after _para1:b,0x4829,0x8da4580
這里加了__block修飾符。通過日志可以看出,block內部_para1的地址、值和執行前的_para1一樣,在block初始化后,對變量_para1的修改,可以同步到block內,block內并不是截獲了變量的值。我們可以看下通過clang 轉換后的后的cpp代碼:
//block 的實現函數的對象
struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr;};
//block 引用的參數對象(每一個參數生成一個結構體)
struct __Block_byref__para1_0 { void *__isa;__Block_byref__para1_0 *__forwarding; int __flags; int __size; char *_para1;};
//block對象
struct __main1_block_impl_0 { struct __block_impl impl; struct __main1_block_desc_0* Desc; __Block_byref__para1_0 *_para1; // by ref __main1_block_impl_0(void *fp, struct __main1_block_desc_0 *desc, __Block_byref__para1_0 *__para1, int flags=0) : _para1(__para1->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};
//block 的實現
static void __main1_block_func_0(struct __main1_block_impl_0 *__cself) { __Block_byref__para1_0 *_para1 = __cself->_para1; // bound by ref printf("exute _para1:%s,%p,%p/n",(_para1->__forwarding->_para1),(_para1->__forwarding->_para1),&(_para1->__forwarding->_para1)); }
//其他函數(copy、dispose:為了管理_Block_byref__para1_0 結構體變量的內存)
static void __main1_block_copy_0(struct __main1_block_impl_0*dst, struct __main1_block_impl_0*src) {_Block_object_assign((void*)&dst->_para1, (void*)src->_para1, 8/*BLOCK_FIELD_IS_BYREF*/);}static void __main1_block_dispose_0(struct __main1_block_impl_0*src) {_Block_object_dispose((void*)src->_para1, 8/*BLOCK_FIELD_IS_BYREF*/);}static struct __main1_block_desc_0 { size_t reserved; size_t Block_size; void (*copy)(struct __main1_block_impl_0*, struct __main1_block_impl_0*); void (*dispose)(struct __main1_block_impl_0*);} __main1_block_desc_0_DATA = { 0, sizeof(struct __main1_block_impl_0), __main1_block_copy_0, __main1_block_dispose_0};
//這是我們的測試函數
void main1(){ __attribute__((__blocks__(byref))) __Block_byref__para1_0 _para1 = {(void*)0,(__Block_byref__para1_0 *)&_para1, 0, sizeof(__Block_byref__para1_0), "a"}; printf("init _para1:%s,%p,%p/n",(_para1.__forwarding->_para1),(_para1.__forwarding->_para1),&(_para1.__forwarding->_para1)); void(*testBlock)(void)=(void (*)())&__main1_block_impl_0((void *)__main1_block_func_0, &__main1_block_desc_0_DATA, (__Block_byref__para1_0 *)&_para1, 570425344); (_para1.__forwarding->_para1)="b"; printf("before _para1:%s,%p,%p/n",(_para1.__forwarding->_para1),(_para1.__forwarding->_para1),&(_para1.__forwarding->_para1)); ((void (*)(__block_impl *))((__block_impl *)testBlock)->FuncPtr)((__block_impl *)testBlock); printf("after _para1:%s,%p,%p/n",(_para1.__forwarding->_para1),(_para1.__forwarding->_para1),&(_para1.__forwarding->_para1));}
發現和不加__block不太一樣,多了一個結構體(_Block_byref__para1_0)
struct __Block_byref__para1_0 { void *__isa;__Block_byref__para1_0 *__forwarding; int __flags; int __size; char *_para1;};
這個結構體就是用__block 聲明的變量,聲明__block變量_para1 轉換后的代碼如下:
__attribute__((__blocks__(byref))) __Block_byref__para1_0 _para1 = {(void*)0,(__Block_byref__para1_0 *)&_para1, 0, sizeof(__Block_byref__para1_0), "a"};
其實就是生成一個_Block_byref__para1_0對象,__forwarding 指針指向變量結構體自己
void(*testBlock)(void)=(void (*)())&__main1_block_impl_0((void *)__main1_block_func_0, &__main1_block_desc_0_DATA, (__Block_byref__para1_0 *)&_para1, 570425344);
初始化block,傳遞結構體變量_para1、函數地址、描述信息等參數,block對象的成員 _para1 引用了_para1結構體的地址,這樣就可以修改_para1,而且外界對_para1結構體的修改都可以同步到block對象的成員_para1中,修改代碼如下:
(_para1.__forwarding->_para1)="b";
直接通過__Block_byref__para1_0 結構體的成員__forwarding(指向自己)取得_para1成員的地址,然后就可以讀寫,達到了修改變量的目的。
((void (*)(__block_impl *))((__block_impl *)testBlock)->FuncPtr)((__block_impl *)testBlock);
執行block,依然是通過調用函數指針 FuncPtr 實現,并傳遞block自身。函數體代碼如下:
static void __main1_block_func_0(struct __main1_block_impl_0 *__cself) { __Block_byref__para1_0 *_para1 = __cself->_para1; // bound by ref printf("exute _para1:%s,%p,%p/n",(_para1->__forwarding->_para1),(_para1->__forwarding->_para1),&(_para1->__forwarding->_para1)); }
在函數體內,通過block對象的成員_para1(__Block_byref__para1_0)的指針,再通過自身的成員__forwarding指針來獲取_para1。
我的理解就是指針引用的關系,不知道為什么要加一個__forwarding指針 ,直接去引用自身的成員 _para1難道不行嗎?希望大神賜教。
新聞熱點
疑難解答