上一篇文章中对于block做了一个全面的剖析和理解,那么在OBjective—C的block使用中我们难免会用到以上几个关键字,其实对于_block上篇文章已经做了解释,这篇文章,我会做几个关键字的区别和总结,来加深认识和理解。
*上篇文章知道 clang -rewrite-objc 可以将OC代码转化成C代码C++代码,如果变量加上 weak修饰,会发现无法转化,提示:cannot create _* weak reference because the current deployment target does not support weak references 我们需要这样解决这个问题:clang -rewrite-objc -fobjc-arc -fobjc-runtime=macosx-10.13 main.m -fobjc-arc代表当前是arc环境 -fobjc-runtime=macosx-10.13:代表当前运行时环境 缺一不可 clang指令**
.m 源码
int main(int argc, char * argv[]) {
// @autoreleasepool {
// return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
int a = 10;
int b = 20;
NSNumber *c = @(123);
__block NSNumber *d = c;
__weak NSNumber *f = c;
void (^myBlock)(void) = ^{
__strong NSNumber *e = f;
NSLog(@"hello world %d, %d, %@, %@, %@, %@", a, b, c, d, f, e);
};
myBlock();
// }
return 0;
}
转化成c代码
// block 结构体
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int a;
int b;
NSNumber *__strong c;
NSNumber *__weak f;
__Block_byref_d_0 *d; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int _b, NSNumber *__strong _c, NSNumber *__weak _f, __Block_byref_d_0 *_d, int flags=0) : a(_a), b(_b), c(_c), f(_f), d(_d->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
// 我们可以看到没有__block修饰的变量和__weak修饰的对象,都没有重新生成一个__Block_byref_d_0 的结构体,只有__block修饰的才会生成这样的结构体
int main(int argc, char * argv[]) {
int a = 10;
int b = 20;
NSNumber *c = ((NSNumber *(*)(Class, SEL, int))(void *)objc_msgSend)(objc_getClass("NSNumber"), sel_registerName("numberWithInt:"), (123));
__attribute__((__blocks__(byref))) __Block_byref_d_0 d = {(void*)0,(__Block_byref_d_0 *)&d, 33554432, sizeof(__Block_byref_d_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, c};
__attribute__((objc_ownership(weak))) NSNumber *f = c;
void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, f, a, b, c, (__Block_byref_d_0 *)&d, 570425344));
((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
return 0;
}
struct __Block_byref_d_0 {
void *__isa;
__Block_byref_d_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
NSNumber *__strong d;
};
// 我们可以看到没有加__block的变量,在捕获之后,都会copy出来,不管是值类型还是引用类型的,所以不可以改变内容
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_d_0 *d = __cself->d; // bound by ref
NSNumber *__weak f = __cself->f; // bound by copy
int a = __cself->a; // bound by copy
int b = __cself->b; // bound by copy
NSNumber *__strong c = __cself->c; // bound by copy
__attribute__((objc_ownership(strong))) NSNumber *e = f;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_dr_jkts5c395zs9xcx0bt_r7fx40000gn_T_main_188e34_mi_0, a, b, c, (d->__forwarding->d), f, e);
}
通过上述的源码,我们可以看出,只有__ block 修饰的局部变量才会生成一个结构体,这个结构体包含了变量,而block的结构体则是一个变成了结构体的指针,所以内部修改指针指向内容是可以的,而对于没有修饰的变量,值变量则是重新声明一个变量赋值相同,引用类型则是重新申明变量,指针copy,所以在内部都是无法修改外部变量的值的。
综上所述,__ block :修饰的外部局部变量值和引用类型,都会重新生成包裹该变量的结构体,block本质结构体会声明这个结构体的指针,所以可以直接修改外部局部变量;可修饰值变量和引用变量(即普通类型和对象类型的变量);
没有修饰的变量:值类型和引用类型都会重新生成一个变量,值类型重新生成一个变量,将局部变量的值赋值给他;引用类型的变量,生成一个新指针,指向这个指针的copy,强引用;
weak :没有重新生成包裹变量的结构体,重新声明一个弱引用指针,指向这个指针的copy体,所以不可以在内部修改变量的值;只能修饰引用类型的变量,所以 weak 修饰可以防止循环引用;
__strong:一般使用像如下代码
// typeof() 函数只是返回参数的类型
__weak typeof(self) weakSelf = self;
void (^myBlock)(void) = ^{
__strong typeof(self) strongSelf = weakSelf;
}
block内部对变量生成一个强引用,防止在Block函数体执行过程中,变量被释放,block函数体执行完,引用计数器-1