源代碼:
int main(int argc, const char * argv[])
{
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;
}
将上面的源代碼用指令:“clang -rewrite-objc main.c”轉換為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; // bound by copy
int val = __cself->val; // bound by copy
printf(fmt, val);
}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, const char * argv[])
{
int dmy = 256;
int val = 10;
const char *fmt = "val = %d\n";
void (*blk)(void) = (void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, fmt, val);
val = 2;
fmt = "These values were changed. val = %d\n";
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
return 0;
}
這與前面轉換的源代碼稍有差異。下面來看看其中的不同這處。首先我們注意到,Block語句中使用的局部變量被作為成員變量追加到了__main_block_impl_0結構體中。
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
const char *fmt;
int val;
}
__main_block_impl_0結構體内聲明的成員變量類型與局部變量類型完全相同。請注意,Block語句中沒有使用的局部變量不會被追加,如此源代碼中的變量dmy。Blocks的局部變量截獲隻針對Block中使用的局部變量。下面來看看初始化該結構體執行個體的構造函數的差異。
void (*blk)(void) = (void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, fmt, val);
使用執行Block文法時的局部變量fmt和val來初始化__main_block_impl_0結構體執行個體。
即在該源代碼中,__main_block_impl_0結構體執行個體的初始化如下:
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = __main_block_desc_0_DATA;
fmt = "val = %d\n";
val = 10;
由此可知,在__main_block_impl_0結構體執行個體(即Block)中,變量值被截獲。
下面再來看一下使用Block的匿名函數的實作。最被源代碼的Block文法如下所示:
printf(fmt, val);
該源代碼可轉換為以下函數:
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
const char *fmt = __cself->fmt; // bound by copy
int val = __cself->val; // bound by copy
printf(fmt, val);
}
在轉換後的源代碼中,截獲到__main_block_impl_0結構體執行個體的成員變量上的局部變量,這些變量在Block文法表達式之前被聲明定義。是以,原來的源代碼表達式無需改動便可使用截獲的局部變量值執行。
總的來說,所謂“截獲局部變量值”意味着在執行Block文法時,Block文法表達式所使用的局部變量值被儲存到Block的結構體(即Block自身)中。
然而,Block不能直接使用C文法數組類型的局部變量。如前所述,截獲局部變量時,将值傳遞給結構體的構造函數進行儲存。
下面确認在Block中利用C文法數組類型的變量時有可能使用到的源代碼。首先來看将數組傳遞給Block的結構體構造函數的情況。
void func(char a[10]) {
char b[10] = a;
printf("%d\n", b[0]);
}
int main() {
char a[10] = {2};
func(a);
}
該源代碼可以順利編譯,并正常執行。在之後的構造函數中,将參數賦給成員變量中,這樣在變換了Block文法的函數内可由成員變量指派給局部變量。源代碼預測如下。
void func(char a[10]) {
char b[10] = a;
printf("%d\n", b[0]);
}
int main() {
char a[10] = {2};
func(a);
}
該源代碼将C語言數組類型變量指派給C語言數組類型變量中,這是不能編譯的。雖然變量的類型以及數組的大小都相同,但C語言規範不允許這種指派。當然,有許多方法可以截獲值,但Blocks似乎更遵循C語言規範。
}