Block截獲自動變量的情況
</br>
Objective-C語句:
int main() {
int dmy = 256;
int val = 10;
const char *fmt = "val = %d\n";
void (^blk)(void) = ^{ printf(fmt, val); };
val = 2;
fmt = "These values were changed. val = %d\n";
blk();
return 0;
}
C++源碼:(注:有一部分與上例相同的代碼已省略)
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0 *Desc;
const char *fmt;
int val;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const char *_fmt, int _val, int flags=0) : fmt(_fmt), val(_val) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0 (struct __main_block_impl_0 *__cself) {
const char *fmt = __cself->fmt;
int val = __cself->val;
printf(fmt, val);
}
static struct __main_block_desc_0 {
unsigned long reserved;
unsigned long Block_size;
} __main_block_desc_0_DATA = {
0,
sizeof(struct __main_block_impl_0)
};
int main() {
int dmy = 256;
int val = 10;
const char *fmt = "val = %d\n";
void (*blk)(void) = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, fmt, val);
}
Block數據結構圖(4)
整體看,__main_block_impl_0
結構體、__main_block_func_0
函數、和main
函數內blk
對構造函數的調用都發生了改變:
-
__main_block_impl_0
結構體內聲明的成員變量與自動變量的類型完全相同const char *
和int
。同時發現很重要的一點,dmy
變量沒有被追加到__main_block_impl_0
結構體的成員變量中。為什么?因為Block沒有使用它。由此得知,Block對自動變量的截獲只針對會在Block內使用的自動變量。 同時,該結構體的構造函數也對截獲的自動變量追加的成員變量進行了初始化操作。
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const char *_fmt, int _val, int flags=0) : fmt(_fmt), val(_val) { ... }
-
main
函數內的構造函數調用中也添加了相應的參數
void (*blk)(void) = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, fmt, val)
-
__main_block_func_0
函數
static void __main_block_func_0 (struct __main_block_impl_0 *__cself) {
const char *fmt = __cself->fmt;
int val = __cself->val;
printf(fmt, val);
}
由于在main
中blk
調用構造函數時已經截獲到自動變量,并被保存到Block結構體的實例中,所以可以直接使用__cself->fmt
和__cself->val
。
至此,可以解釋兩件事情:
第一,為什么在Block截獲了自動變量之后,在外部修改自動變量的值,不會影響Block截獲到的值。
答:見代碼注釋部分的文字。
int dmy = 256;
int val = 10;
const char *fmt = "val = %d\n";
void (^blk)(void) = ^{ printf(fmt, val); }; //此處完成自動變量的截獲
val = 2; //在Block外修改自動變量的值,不會影響Block內已經保存的值
fmt = "These values were changed. val = %d\n";
blk();
第二,為什么Block截獲的自動變量不能被改寫。
答:因為在實現上不能改寫被截獲自動變量的值。
int val = 0;
void (^blk)(void) = ^{ val = 1; };
我們知道賦值語句^{ val = 1; }
實際上是在調用Block的構造函數,但是在實現構造函數中并沒有參數位置可以保存這個要賦值給val
的1
。對于這種行為,編譯器在編譯過程會檢測出,從而產生編譯錯誤。