针对block的常见应用,从底层出发,列举了如下几个经典的问题。
- block 全局变量和自动变量;
- block变量的引用;
一、block内部修改外部的变量方式
在myBlock修改age值为20的办法及其原理?
int age = 10;
void(^myBlock)(void) = ^{
NSLog(@"age--%d",age);
};
myBlock();
1、在age变量添加修饰符static,这样一来myBlock 内部捕获的成员变量将是 int *age,捕获的内存地址,通过地址传递,就可以直接修改age的值。 其myBlock的结构如下:
struct __main_block_impl_0{
struct __block_impl;
struct __block_des;
int * age; //指针
}
2、将age变成全局变量,这样block即可访问并使用改变量。全局变量任何函数任何地方都可调用。
3、通过__block关键字来修饰。其底层原理:__block会将age封装成一个 __Block_byref_age_0 的一个对象,对象内部有一个 __Block_byref_age_0 的 __forwarding 指针指向自己,所以在修改值的时候会执行 age->__forwarding->age 在修改和取值,这样一来就达到了修改的操作。
block本质结构如下:
struct __main_block_impl_0{
...
__Block_byref_age_0 *age;//本质是对象
}
__Block_byref_age_0 本质结构如下:
struct __Block_byref_age_0{
void * __isa;
__Block_byref_age_0 *__forwarding;
int __flags;
int __size;
int age;
}
二、block变量的引用。
WSTObjct * wstObj = [[WSTObjct alloc] init];
__weak WSTObjct * blockWstObj = wstObj;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"1------%@",blockWstObj);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"2------%@",wstObj);
});
});
NSLog(@"touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event");
WSTObject 在销毁时,我们打印了dealloc
@implementation WSTObjct
- (void)dealloc
{
NSLog(@"WSTObjct 对象销毁");
}
@end
执行代码后,WSTObjct 对象销毁 在第几秒后打印?
答案:第三秒后打印,图下图所示;
首先wstObj是局部变量,block会进行捕获变量到__main_block_impl。编译器会发现第二个dispatch_after中的wstObj和block中进行了强引用,所以block的销毁时wstObj才销毁。