更新记录
Block引用C语言数组报错
char text[] = "hello";
void (^blk)(void) = ^{
printf("%c\n",text[2]);
};
- 报编译错误:error:cannot refer to declaration with an array type inside block
- 《Objective-C 高级编程 iOS与OS X多线程和内存管理》2.3.1 Block的实质中,提出解决方案,修改成如下代码:
char text[] = "hello";
int main(int argc, const char * argv[]) {
void (^blk)(void) = ^{
printf("%c\n",text[2]);
};
return 0;
}
报错的根本原因
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
const char *fmt;
int val; //block捕获的变量 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;
}
};
- 上述的block,捕获了一个const char*和一个int。所以构造函数中会多两个参数。
- 上述就是目前编译器的实现,所以如果捕获一个C语言数组,会变成如下代码:
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
const char *fmt;
int val; //block捕获的变量 val
char szArray[10];
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const char *_fmt, int _val, char _szArray[10], int flags=0) : fmt(_fmt), val(_val), szArray[10](_szArray) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
- 由于转换成了构造函数的参数列表的形式,实质上就是调用了
szArray[10] = _szArray
这样的语句。虽然变量的类型以及数组的大小都相同,但C语言规范不允许这种赋值。因此会报编译错误。
捕获指针替代捕获C语言数组
- 上述可以正常编译的代码,实际对应的转换后的C/C++源代码如下:
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
char *text;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, char *_text, int flags=0) : text(_text) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
char *text = __cself->text; // bound by copy
printf("%c\n",text[2]);
}
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[]) {
char text[] = "hello";
__main_block_impl_0 block_struct = __main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, text);
void (*blk)() = (void (*)())&block_struct;
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
return 0;
}
- 因为对于指针的赋值
text = _text
是符合C语言语法规范的,因此这种用法是没有任何问题的。
阅读延申
char text[] = "hello";
int main(int argc, const char * argv[]) {
void (^blk)(void) = ^{
printf("%c\n",text[2]);
};
return 0;
}
- 但是,它的原因是因为:这个Block对象里面使用的是全局变量,它是不需要存储到自己的Block结构体中,即它的结构体应该是如下结构:
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
//如果block截获了自动变量,会放置在这里。由于该block内引用的是全局变量,并不会在此加入字段进行初始化。
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, char *_text, int flags=0) : text(_text) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
- 虽然博文的结果具有参考性,但是需要特别区分好这两种做法的底层特性。
参考资料
- 《Objective-C 高级编程 iOS与OS X多线程和内存管理》2.3.1 Block的实质
- 《Objective-C 高级编程 iOS与OS X多线程和内存管理》2.3.2 截获自动变量值