OC对象 - Block-对象类型的auto变量
1. Block内部访问了对象类型的auto变量
当block
内部访问了对象类型的auto
变量时,此时block可能在栈上
,也可能在堆上
,不同类型
的block所表现的动作是不一样的
1.1 基础代码
创建一个ZSXPerson
类,在- (void)dealloc
打印一下,这样我们可以清楚看到ZSXPerson
对象什么时候销毁
less
@interface ZSXPerson : NSObject
@end
@implementation ZSXPerson
- (void)dealloc {
[super dealloc];
NSLog(@"ZSXPerson --- %s", __func__);
}
@end
main.m
csharp
int main(int argc, const char * argv[]) {
@autoreleasepool {
{
ZSXPerson *person = [[ZSXPerson alloc] init];
}
NSLog(@"-----");
}
return 0;
}
在{}
内,初始化一个ZSXPerson
对象,然后在在{}
外打了断点
可以看到person
对象出了{}
会马上销毁
接下来我们在此基础上,尝试不同 block 对person
对象销毁时机的影响
1.2 Block访问person对象
增加一个block,内部访问person对象
ini
ZSXBlock block;
{
ZSXPerson *person = [[ZSXPerson alloc] init];
person.age = 10;
block = ^ {
NSLog(@"%d", person.age);
};
}
NSLog(@"block - %@", [block class]);
NSLog(@"-----");
此时断点停留在{}
之后,按理说 person 已经出了作用域了,但实际却没有销毁
我们断点继续往下走 出了@autoreleasepool
之后,person
才销毁。这时候刚好 block 也已经出了作用域,block
是会销毁的。因此,block
里面对person
有强引用,所以出了第一个{}
的时候,虽然person
已经走出作用域,但是此时block
还在作用域内,它还持有person
,所以person
并不会释放
1.2.1 查看底层实现
block访问对象类型的auto变量后
- block结构体中持有
person
成员的指针 __main_block_desc_0
结构体中多了copy
和dispose
两个函数。这两个函数是用来管理block
所持有变量的持有关系的
因为block里面持有了对象,对象本身在内存中是通过引用计数
来管理内存的,因此block也需要对其负责内存管理
1.3 NSStackBlock
类型block访问对象类型
NSStackBlock
类型block本身就是随时可能释放的,所以NSStackBlock
类型的block没有必要强持有访问对象
1.3.1 修改代码
代码中我们不使用
strong变量接收block,这时候的block就是NSStackBlock
类型的
ini
ZSXBlock block;
{
ZSXPerson *person = [[ZSXPerson alloc] init];
person.age = 10;
^ {
NSLog(@"%d", person.age);
};
}
NSLog(@"block - %@", [block class]);
NSLog(@"-----");
打印结果: person
出了作用域马上就释放了,因此可以说明这时候的block
并没有持有person
。
2. 总结
当block
内部访问了对象类型的auto
变量时
-
如果block是在
栈上
,将不会
对auto变量产生强引用 -
如果block被
拷贝到堆上
- 会调用block内部的
copy
函数 - copy函数内部会调用
_Block_object_assign
函数 - _Block_object_assign函数会根据auto变量的
修饰符
(__strong、__weak、__unsafe_unretained)做出相应
的操作,形成强引用
(retain)或者弱引用
- 会调用block内部的
-
如果block从堆上
移除
- 会调用block内部的
dispose
函数 - dispose函数内部会调用
_Block_object_dispose
函数 - _Block_object_dispose函数会
自动释放
引用的auto变量(release)
- 会调用block内部的
@oubijiexi