__block 修饰符的使用
__block可以用于解决block内部无法修改auto变量值的问题
__block不能修饰全局变量、静态变量(static)
编译器会将__block变量包装成一个对象
以下代码的是否编译通过,可以的话输出结果是什么
int a = 10;
void (^block)() = ^{
a = 20;
NSLog(@"a = %d",a);
};
结果:无法编译 miss__block
源码如下
//main函数
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
int a = 10;
void (*block)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a));
}
return 0;
}
//block执行地址
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int a = __cself->a; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_kh_0rp73c0s2mvfp5gjf25j5y6h0000gn_T_main_1a12fa_mi_0,a);}
block执行的时候,内部是 __main_block_func_0 函数,而a的声明,是在main函数,两个函数相互独立,对于他们来说,a都是一个局部变量,而且两个函数中都对a初始化,两个函数的中a不是同一个,那怎么可以在 执行函数中,修改main函数中的局部变量呢,所以编译报错!
如何改?
方案一:使用static
static int a = 10;
void (^block)() = ^{
a = 20;
NSLog(@"a = %d",a);
};
因为static修饰的auto变量,最终在block中进行的不是值传递,而是地址传递,措意执行函数中的a 和 main 函数中的a,是同一个地址 ==> 等于同一个a,所以可以修改,输出20
但是使用static,就会变成静态变量,永远在内存中
方案二: 使用__blcok
__block auto int a = 10;
void (^block)() = ^{
a = 20;
NSLog(@"a = %d",a);
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_a_0 *a; // by ref ==> auto的话,是int a,__block,变成对象了
}
struct __Block_byref_a_0 {
void *__isa;
__Block_byref_a_0 *__forwarding;==> 指向自己的结构体
int __flags;
int __size;
int a; ==> 10在这里
};
a = 20;最终转成 (a->__forwarding->a) = 20;
解释下:__forwarding 是指向结构体本身的指针,等价于a本身,其实就是通过a的结构体指针,拿到里面的成员a,再对他赋值
指针传递,所以可以修改 auto 变量,通过block,间接引用 auto 变量
forwarding指针__block中的 _ forwarding 指针
内存拷贝的时候,如果block从栈被copy到堆上,肯定也希望内部的变量一起存储到堆上(让变量的生命周期可控,才不会被回收)
加入变量a在栈上,在栈上的指针,指向堆上的 block,堆上的block的 forwarding指向他自己,就可以保证,修改&获取的变量,都是堆上的变量
最终,__block指向的变量,是指向堆上的
forwarding指针.png一旦block里面要用到/访问某个对象,那就要对相应的对象进行内存管理
当block在栈上的时候,是不会对__block变量进行强引用的
当block在堆上的时候,会调用block内部的copy函数,而copy函数内部会调用_Block_object_assign函数,_Block_object_assign函数会根据auto变量的修饰符 ( strong、 weak、unsafe_unretained ) 做出对应的操作,看对内部auto变量进行强引用还是弱引用
对象类型的auto变量和__block变量
当block在栈上的时候,对他们都不会产生强引用,永远都是弱引用,
当block被copy到堆上来的时候,都会通过copy函数来处理它们_Block_object_assign-》1.weak 2.strong
移除:
当block从堆中移除的时候,会调用block内部的dispose函数,而dispose函数内部会调用_Block_objcet_dispose函数,_Block_object_dispose 类似于 release,会对auto变量进行自动释放(当引用计数器=0的时候 )
上图
Tips:
在使用clang转换OC为C++代码时,可能会遇到以下问题
cannot create __weak reference in file using manual reference
解决方案:支持ARC、指定运行时系统版本,比如
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m
网友评论