(一)copy
ARC环境下,编译器会根据情况自动将stackblock进行copy操作,复制到堆上
- block作为函数返回值时
- 将block赋值给__strong指针时
- block作为Cocoa API中方法名含有usingBlock的方法参数时
- block作为GCD API的方法参数时
//1 2 示例省略
//block作为GCD API的方法参数时
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
ZQPerson *person = [[ZQPerson alloc] init];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"---------- %@",person);
});
}
//block作为Cocoa API中方法名含有usingBlock的方法参数时
NSArray *array = @[@(5), @(8), @(10)];
[array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"%d %@",idx,obj);
}];
(1)block属性的常用写法(copy strong进行强引用)
-
MRC下block属性的建议写法
@property (copy, nonatomic) void (^block)(void); -
ARC下block属性的建议写法
@property (strong, nonatomic) void (^block)(void);
@property (copy, nonatomic) void (^block)(void);
(2) block内部访问对象类型的auto变量
-
block是在栈上(stackBlock),将不会对auto变量产生强引用
-
block被拷贝到堆上
- 会调用block内部的copy函数
- copy函数内部会调用
_Block_object_assign
函数 -
_Block_object_assign
函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained
)做出相应的操作,形成强引用(retain)或者弱引用
- 如果block从堆上移除
- 会调用block内部的dispose函数
- dispose函数内部会调用
_Block_object_dispose
函数 -
_Block_object_dispose
函数会自动释放引用的auto变量(类似release
,断开block对变量的持有)
block内部访问对象类型的auto类型的变量②
(二)weak
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
ZQPerson *person = [[ZQPerson alloc] init];
__weak ZQPerson *weakPerson = person;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"---------- %@",weakPerson);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"---------- %@",person);
});
});
}
//输出结果:
/*block对weakPerson弱引用,但是因为后面的person变量被强引用,所以person还没有被释放
---------- <ZQPerson: 0x60000189c7b0>
---------- <ZQPerson: 0x60000189c7b0>
dealloc
*/
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
ZQPerson *person = [[ZQPerson alloc] init];
__weak ZQPerson *weakPerson = person;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"---------- %@",person);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"---------- %@",weakPerson);
});
});
}
//输出结果:
/*person变量被强引用,后面的block对weakPerson弱引用,此时person已被释放
---------- <ZQPerson: 0x6000005c3eb0>
dealloc
---------- (null)
*/
通过上面的两个代码。我们可以看到结果不同
在使用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-9.0.0 main.m
(三)__block
(1)block修改变量
- 在block内部修改auto类型的变量是报错的
- 在block内部可以修改static类型的变量,但是永久在内存中
-
在block内部可以修改全局变量
image.png
如何将在block内部修改auto类型的变量呢?__block
__block int age = 10;
ZQBlock block = ^{
age = 20;
NSLog(@"%d",age);
};
block();
(2)__block的本质
- __block可以用于解决block内部无法修改auto变量值的问题
- __block不能修饰全局变量、静态变量(static)
- 编译器会将__block变量包装成一个对象
__block的底层结构
改值
疑问(1):如果auto变量是对象?
如果是对象,则会多两个函数疑问(2):如果block内部操作对象属性/方法?
//不需要使用__block 能不加就不加 因为会被多包装一层
NSMutableArray *array = [NSMutableArray array];
ZQBlock block = ^{
[array addObject:@"123"];
};
block();
疑问(3):如下面的代码,block调用完后,继续访问age、person变量,他们的地址存放在哪里?
__block int age = 10;
__block ZQPerson *person = [[ZQPerson alloc] init];
NSLog(@"1 %d %p %@",age,&age, person);
ZQBlock block = ^{
age = 20;
person = [[ZQPerson alloc] init];
NSLog(@"2 %d %p %@",age,&age, person);
};
block();
NSLog(@"3 %d %p %@",age,&age, person);
/*输出结果:
1 10 0x7ffeefbff558 <ZQPerson: 0x100508850>
2 20 0x1005093b8 <ZQPerson: 0x1005093f0>
3 20 0x1005093b8 <ZQPerson: 0x1005093f0>
*/
答:
访问的age和person其实是block创建的对象中的age与person
网友评论