block本质 block是封装了函数调用以及函数调用环境的 OC对象
__block的作用
- 编译器会将__block变量包装成一个对象,可以用于解决block内部无法修改auto变量值的问题
- __block不能修饰全局变量,静态变量(static)
1.在ARC环境下,编译器会根据情况自动将栈上的block复制到堆区,比如以下情况
- block作为函数返回值时
return ^{ } ;
- 将block赋值给__strong指针时
MBlock bolck = ^{ };
- block作为cocoa API中方法名含有useingBlock的方法参数时
[array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
}] ;
- block作为GCD API的方法参数时
dispatch_once(&onceToken, ^{
});
block一旦没有进行copy操作,就不会在堆上,就无法控制block的生命周期,使用时注意循环引用
block在修饰NSMutableArray,如果只是修改数组内的对象(比如addObject:),不需要添加__block
截屏2020-08-18 上午10.45.40.png
截屏2020-08-17 下午5.50.11.png
先看一个block捕获外部auto变量age,通过命令clang xxx.m -rewrite-objc -o xxx.cpp或者xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-9.0.0 XX.m 将.m文件转成 c++
OC代码
int c = 10 ;
static int d = 10 ;
void test(){
auto int a = 10 ;
static int b = 10 ;
void (^rBlock)(void) = ^{
NSLog(@"a = %d , b = %d , c = %d , d= %d",a,b,c,d) ;
} ;
a = 20 ;
b = 20 ;
rBlock() ;
}
int main(int argc, char * argv[]) {
@autoreleasepool {
test() ;
}
return 0;
}
C++代码
int c = 10 ;
static int d = 10 ;
struct __test_block_impl_0 {
struct __block_impl impl;
struct __test_block_desc_0* Desc;
int a;
int *b;
__test_block_impl_0(void *fp, struct __test_block_desc_0 *desc, int _a, int *_b, int flags=0) : a(_a), b(_b) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __test_block_func_0(struct __test_block_impl_0 *__cself) {
int a = __cself->a; // bound by copy
int *b = __cself->b; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_rg_dnyfk4m119n4q8vj670_shn00000gn_T_main_34442b_mi_0,a,(*b),c,d) ;
}
static struct __test_block_desc_0 {
size_t reserved;
size_t Block_size;
} __test_block_desc_0_DATA = { 0, sizeof(struct __test_block_impl_0)};
void test(){
auto int a = 10 ;
static int b = 10 ;
void (*rBlock)(void) = ((void (*)())&__test_block_impl_0((void *)__test_block_func_0, &__test_block_desc_0_DATA, a, &b)) ;
a = 20 ;
b = 20 ;
((void (*)(__block_impl *))((__block_impl *)rBlock)->FuncPtr)((__block_impl *)rBlock) ;
}
可以看出上面捕获的auto变量有些差别,auto变量被捕获时直接传入的age的值,而static变量被捕获时传入的是age的地址(&age),由于auto变量出了当前作用域内存就会被销毁,所以需要将auto变量的值捕获住,static变量是一直存在内存中,但出了作用域就访问不了啦,所以只需要捕获static变量的内存地址就可以了
全局变量是不会被捕获的,因为全局变量在哪里都可以访问,不需要进行捕获
所以得出的结论是:局部变量是一定会被捕获,全局变量不会被捕获;
局部变量的捕获方式:auto变量是值捕获,static是地址捕获
@implementation KLPerson
-(void)test{
void(^rBlock)(void) = ^{
NSLog(@"self === %@ , name = %@",self,self.rName) ;
};
rBlock();
}
- (instancetype)initWithName:(NSString*)name
{
self = [super init];
if (self) {
self.rName = name ;
}
return self;
}
@end
struct __KLPerson__test_block_impl_0 {
struct __block_impl impl;
struct __KLPerson__test_block_desc_0* Desc;
KLPerson *self;
__KLPerson__test_block_impl_0(void *fp, struct __KLPerson__test_block_desc_0 *desc, KLPerson *_self, int flags=0) : self(_self) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __KLPerson__test_block_func_0(struct __KLPerson__test_block_impl_0 *__cself) {
KLPerson *self = __cself->self; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_rg_dnyfk4m119n4q8vj670_shn00000gn_T_KLPerson_ce4a47_mi_0,self,((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("rName"))) ;
}
static void __KLPerson__test_block_copy_0(struct __KLPerson__test_block_impl_0*dst, struct __KLPerson__test_block_impl_0*src) {_Block_object_assign((void*)&dst->self, (void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static void __KLPerson__test_block_dispose_0(struct __KLPerson__test_block_impl_0*src) {_Block_object_dispose((void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static struct __KLPerson__test_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __KLPerson__test_block_impl_0*, struct __KLPerson__test_block_impl_0*);
void (*dispose)(struct __KLPerson__test_block_impl_0*);
} __KLPerson__test_block_desc_0_DATA = { 0, sizeof(struct __KLPerson__test_block_impl_0), __KLPerson__test_block_copy_0, __KLPerson__test_block_dispose_0};
static void _I_KLPerson_test(KLPerson * self, SEL _cmd) {
void(*rBlock)(void) = ((void (*)())&__KLPerson__test_block_impl_0((void *)__KLPerson__test_block_func_0, &__KLPerson__test_block_desc_0_DATA, self, 570425344));
((void (*)(__block_impl *))((__block_impl *)rBlock)->FuncPtr)((__block_impl *)rBlock);
}
static instancetype _Nonnull _I_KLPerson_initWithName_(KLPerson * self, SEL _cmd, NSString * _Nonnull name) {
self = ((KLPerson *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("KLPerson"))}, sel_registerName("init"));
if (self) {
((void (*)(id, SEL, NSString * _Nonnull))(void *)objc_msgSend)((id)self, sel_registerName("setRName:"), (NSString * _Nonnull)name) ;
}
return self;
}