標籤:des style blog color 使用 os 檔案 io
局部變數block內使用局部變數,一般都是截獲變數(唯讀),截獲離block初始化最近的一次的值。
引用官方文檔:
- Stack (non-static) variables local to the enclosing lexical scope are captured as const variables.Their values are taken at the point of the block expression within the program. In nested blocks, the value is captured from the nearest enclosing scope.
我們做一個測試,瞭解一下原理代碼如下:
void main1(){ 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,0x97f70,0x27d70988before _para1:b,0x97fa5,0x27d70988exute _para1:a,0x97f70,0x165839e4after _para1:b,0x97fa5,0x27d70988
我們可以藉助 clang -rewrite-objc 轉換.c檔案得到.cpp檔案,也可以轉換.m也可以得到cpp檔案(可能會有些報錯)以下是部分轉換後的代碼//這裡就是block對象的結構
//imp:函數指標對象,FuncPtr指向具體block實現的函數
//_person2:截獲的變數
//isa、flags、funcptr、desc後面會說道。
struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr;};
struct __main1_block_impl_0 { struct __block_impl impl; struct __main1_block_desc_0* Desc; char *_para1; __main1_block_impl_0(void *fp, struct __main1_block_desc_0 *desc, char *__para1, int flags=0) : _para1(__para1) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};
//block實現的函數
static void __main1_block_func_0(struct __main1_block_impl_0 *__cself) { char *_para1 = __cself->_para1; // bound by copy printf("exute _para1:%s,%p,%p\n",_para1,_para1,&_para1); }
//block對象的描述資訊(大小等等)
static struct __main1_block_desc_0 { size_t reserved; size_t Block_size;} __main1_block_desc_0_DATA = { 0, sizeof(struct __main1_block_impl_0)};
//這是objc測試函數test
void main1(){ char *_para1="a"; printf("init _para1:%s,%p,%p\n",_para1,_para1,&_para1); void(*testBlock)(void)=(void (*)())&__main1_block_impl_0((void *)__main1_block_func_0, &__main1_block_desc_0_DATA, _para1); _para1="b"; printf("before _para1:%s,%p,%p\n",_para1,_para1,&_para1); ((void (*)(__block_impl *))((__block_impl *)testBlock)->FuncPtr)((__block_impl *)testBlock); printf("after _para1:%s,%p,%p\n",_para1,_para1,&_para1);}
簡單分析block截獲變數:
1).block初始化
void(*testBlock)(void)=(void (*)())&__main1_block_impl_0((void *)__main1_block_func_0, &__main1_block_desc_0_DATA, _para1);
傳入了參數:函數指標、block描述、外部變數_para1,這時候在block內部對_para1進行了引用,即block的結構體成員_para1引用了的是 這個地址 0x97f70
: _para1(__para1)
之後又對_para1做了修改,重新指向了 0x97fa5這塊記憶體,但是不會影響block結構體成員_para1,因為成員_para1指向的是0x97f70。
2).執行block
((void (*)(__block_impl *))((__block_impl *)testBlock)->FuncPtr)((__block_impl *)testBlock);
其實還是調用了block對象裡的函數對象(_block_imp1)的函數指標(FuncPtr) 所指向的函數__main1_block_func_0,並把block自己作為參數傳遞進去。
static void __main1_block_func_0(struct __main1_block_impl_0 *__cself) { char *_para1 = __cself->_para1; // bound by copy printf("exute _para1:%s,%p,%p\n",_para1,_para1,&_para1); }
值得注意的的是,我們在執行之前對_para1修改,但是並沒有對block內部產生影響(只是把局部變數_para1重新指向另一塊記憶體(0x97fa5)),而在構造block的時候,block對象內部對截獲的變數做了引用(引用的是0x97f70),放在(char *_para1)裡,執行block的時候,直接調用函數__main1_block_func_0,函數內部,是引用了block結構體的成員_para1,所以會有這樣的結果。