在block函数体里面修改变量在日常开发中常见,我们可以轻松在block体内部修改static变量或全局变量,但是却无法修改auto变量。尝试在block中修改auto变量,编译器错误如下:
block内部修改变量值.png有三种解决方案:
①. 将需要修改的变量设置为全局的;
②. 将需要修改的变量设置为static类型;
③. 在需要修改的变量前加上“__block”修饰符。
针对方案①,全局变量定义太多影响代码阅读,全局变量生命周期长,占内存;
针对方案②,static类型变量的生命周期同APP一样长,一直存在于内存中,所以舍弃。
方案③是完美方案.
来看一个简单的demo:
__block修改变量.pngOC转换为C++代码:
void(*block)(void);
struct __Block_byref_age_0 {
void *__isa;
__Block_byref_age_0 *__forwarding;
int __flags;
int __size;
int age;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_age_0 *age; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_age_0 *_age, int flags=0) : age(_age->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_age_0 *age = __cself->age; // bound by ref
(age->__forwarding->age) = 20;
NSLog((NSString *)&__NSConstantStringImpl__var_folders__6_s9x0n6313d99yqk5pltzp6ym0000gn_T_main_3fd458_mi_0,(age->__forwarding->age));
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->age, 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, char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
__attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 10};
block = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_age_0 *)&age, 570425344));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
}
}
__Block_byref_age_0
结构体,根据其内部定义:
struct __Block_byref_age_0 {
void *__isa;
__Block_byref_age_0 *__forwarding;
int __flags;
int __size;
int age;
};
内部包含isa指针,毫无疑问是一个OC对象;
成员__forwarding
指向结构体本身;
成员__size
保存了结构体__Block_byref_age_0
本身的内存大小;
居然有个成员int age
,并且由之前老巢__main_block_impl_0
迁移到此;
再看看__main_block_impl_0
结构体:
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_age_0 *age; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_age_0 *_age, int flags=0) : age(_age->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
内部也有一个age成员,不过是结构体__Block_byref_age_0
类型指针,所以block内部访问的auto变量就是该结构体age指针指向的内容。经过“__block
”修饰之后,基本类型的“age”变量被包装成“__Block_byref_age_0
”结构体对象。
在main()函数中,我们看到"__block int age = 10;
"被定义成:
__attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,
(__Block_byref_age_0 *)&age,
0,
sizeof(__Block_byref_age_0),
10};
同时block被定义成:
block = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_age_0 *)&age, 570425344))
也就是将"__Block_byref_age_0
"类型的“age”传入到block的构造函数__main_block_impl_0
中,因此__main_block_impl_0
中的age
被赋值这样的结构体:
__attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,
(__Block_byref_age_0 *)&age,
0,
sizeof(__Block_byref_age_0),
10};
即:__Block_byref_age_0
中最后一个成员“int age;
”被赋值10,__forwarding
被赋予__Block_byref_age_0
型age
的地址,这里就是结构体本身,与前面讲述的一致。
再看看block内部的代码块:
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_age_0 *age = __cself->age; // bound by ref
(age->__forwarding->age) = 20;
NSLog((NSString *)&__NSConstantStringImpl__var_folders__6_s9x0n6313d99yqk5pltzp6ym0000gn_T_main_3fd458_mi_0,(age->__forwarding->age));
}
OC代码age = 20;
对应的C++代码(age->__forwarding->age) = 20;
,C++代码中有两个不同类型的age变量,前一个age是__Block_byref_age_0
类型的结构体指针,后一个age是__Block_byref_age_0
结构体中的int型age变量。中间的__forwarding
指向的还是__Block_byref_age_0
类型的结构体指针age,即第一个age,具体为什么这么设计,后文会讲到。
兜了这么一圈,终于明白了,原来"__block"将局部变量包装成一个__Block_byref_age_0
结构体对象,结构体中有与局部变量同名同类型的变量。在block体内修改"__block
"变量,通过一系列指针指向关系,最终指向了__Block_byref_age_0
结构体内与局部变量同名同类型的那个成员,并成功修改变量值。
想了解更多iOS学习知识请联系:QQ(814299221)
网友评论