從前面我們知道了block調(diào)用其實(shí)就是函數(shù)的調(diào)用。block本身用結(jié)構(gòu)體做了一些封裝聘裁。那現(xiàn)在又有一個(gè)疑問。block中可以無縫使用外部的變量蔚晨。加上__block關(guān)鍵字還可以在內(nèi)部修改變量又是怎么實(shí)現(xiàn)呢
我們可以把前面的代碼改一下:
int main(int argc, const char * argv[]) {
@autoreleasepool {
int a = 10;
int b = 11;
int c = 13;
int (^block)(int,int) = ^(int a, int b) {
return a+b+c;
};
block(a,b);
}
return 0;
}
同樣使用命令解析得到C++代碼
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int c;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _c, int flags=0) : c(_c) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static int __main_block_func_0(struct __main_block_impl_0 *__cself, int a, int b) {
int c = __cself->c; // bound by copy
return a+b+c;
}
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[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
int a = 10;
int b = 11;
int c = 13;
int (*block)(int,int) = ((int (*)(int, int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, c));
((int (*)(__block_impl *, int, int))((__block_impl *)block)->FuncPtr)((__block_impl *)block, a, b);
}
return 0;
}
我們來看看與之前例子不同的部分:
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int c;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _c, int flags=0) : c(_c) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static int __main_block_func_0(struct __main_block_impl_0 *__cself, int a, int b) {
int c = __cself->c; // bound by copy
return a+b+c;
}
發(fā)現(xiàn)__main_block_impl_0結(jié)構(gòu)體里面多了一個(gè)變量c而在block創(chuàng)建的時(shí)候?qū)?strong>c變量通過構(gòu)造函數(shù)傳了進(jìn)來
int (*block)(int,int) = ((int (*)(int, int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, c));
也就是說c變量實(shí)質(zhì)上是做了一層拷貝儿礼。而在函數(shù)調(diào)用時(shí)通過__cself參數(shù)拿到block的指針并把c拷貝一份局部變量后使用咖杂。
那加了__block關(guān)鍵字之后是怎么實(shí)現(xiàn)在內(nèi)部改變外部值的呢,我們修改代碼如下:
int main(int argc, const char * argv[]) {
@autoreleasepool {
int a = 10;
int b = 11;
__block int c = 13;
int (^block)(int,int) = ^(int a, int b) {
c = 14;
return a+b+c;
};
block(a,b);
}
return 0;
}
同樣使用命令解析:
struct __Block_byref_c_0 {
void *__isa;
__Block_byref_c_0 *__forwarding;
int __flags;
int __size;
int c;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_c_0 *c; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_c_0 *_c, int flags=0) : c(_c->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static int __main_block_func_0(struct __main_block_impl_0 *__cself, int a, int b) {
__Block_byref_c_0 *c = __cself->c; // bound by ref
(c->__forwarding->c) = 14;
return a+b+(c->__forwarding->c);
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->c, (void*)src->c, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->c, 8/*BLOCK_FIELD_IS_BYREF*/);}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
int a = 10;
int b = 11;
__attribute__((__blocks__(byref))) __Block_byref_c_0 c = {(void*)0,(__Block_byref_c_0 *)&c, 0, sizeof(__Block_byref_c_0), 13};
int (*block)(int,int) = ((int (*)(int, int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_c_0 *)&c, 570425344));
((int (*)(__block_impl *, int, int))((__block_impl *)block)->FuncPtr)((__block_impl *)block, a, b);
}
return 0;
}
可以發(fā)現(xiàn)block里面多了一個(gè)指針 __Block_byref_c_0 類型的指針蚊夫。而block外面加了__block 標(biāo)記的變量c被解釋成 __Block_byref_c_0 類型的結(jié)構(gòu)體诉字。并在調(diào)用時(shí)將c的地址傳入block的構(gòu)造函數(shù)中。block解析的結(jié)構(gòu)體也多了一個(gè)指向__Block_byref_c_0對(duì)象的 c 指針用于儲(chǔ)存該地址知纷。這樣就可以在block結(jié)構(gòu)體內(nèi)改變c變量的值了壤圃。