从上些章节block-变量的捕获(caputer)中,详细说了基本类型的auto变量的捕获,现在来了解下,对象类型的auto变量是怎样捕获和底层结构是如何的。
block自动copy的情况
在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上,比如以下情况
-
block
作为函数返回值时 -
block
赋值给__strong
指针时 -
block
作为Cocoa API
中方法名含有usingBlock
的方法参数时 -
block
作为GCD API
的方法参数时
#import <Foundation/Foundation.h>
#import "RMPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
RMPerson *person = [[RMPerson alloc] init];
person.age = 20;
void (^block)(void) = ^ {
NSLog(@"age is %d",person.age);
};
[person release];
NSLog(@"-----------");
}
return 0;
}
----------------- RMPerson.h -----------------
#import <Foundation/Foundation.h>
@interface RMPerson : NSObject
@property (nonatomic, assign) int age;
@end
----------------- RMPerson.m -----------------
#import "RMPerson.h"
@implementation RMPerson
- (void)dealloc {
[super delloc];
NSLog(@"RMPerson-delloc");
}
@end
// MRC 环境 控制台输出
2018-07-03 10:34:39.523223+0800 __block的本质[20912:1898201] RMPerson-delloc
2018-07-03 10:36:17.021712+0800 __block的本质[20912:1898201] -----------
Program ended with exit code: 0
留意上面代码,是在MRC的环境下的代码,当NSLog(@"-----------");
打印前了,RMPerson就释放了,什么原因呢?虽然block访问的是对象类型的auto变量
,但还是访问了auto变量,所以block是属于NSStackBlock,是存在栈空间的,block运行完就会释放,它自己都不知道自己能存活多久,所以是不会作强持有RMPerson的操作。(结论一:如果block是在栈上,将不会对auto变量产生强引用)
#import <Foundation/Foundation.h>
#import "RMPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
RMPerson *person = [[RMPerson alloc] init];
person.age = 20;
void (^block)(void) = [^ {
NSLog(@"age is %d",person.age);
} copy]; // copy操作,从栈中复制到堆中
[person release];
NSLog(@"-----------");
}
return 0;
}
----------------- RMPerson.h -----------------
#import <Foundation/Foundation.h>
@interface RMPerson : NSObject
@property (nonatomic, assign) int age;
@end
----------------- RMPerson.m -----------------
#import "RMPerson.h"
@implementation RMPerson
- (void)dealloc {
[super delloc];
NSLog(@"RMPerson-delloc");
}
@end
// MRC 环境 控制台输出
2018-07-03 10:49:35.425698+0800 __block的本质[21020:1916302] -----------
Program ended with exit code: 0
从上面的控制台输出可以看出,即使程序结束了,RMPerson都没有释放,这是为什么呢? 因为block做了copy操作,从栈中拷贝到了堆中,此时block强引用了RMPerson,所以保住了RMPerson。
下面我们来看一下,底层c++代码是如何堆中的block是如何保住RMPerson的。
从上面源码分析,
如果block被拷贝到堆上时
- 1.会调用block内部的copy函数
- 2.copy函数内部会调用_Block_object_assign函数
- 3._Block_object_assign函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用。
如果block从堆上移除
- 1.会调用block内部的dispose函数
- 2.dispose函数内部会调用_Block_object_dispose函数
- 3._Block_object_dispose函数会自动释放引用的auto变量(release)
总结:
当block内部访问了对象类型的auto变量时
1.如果block是在栈上,将不会对auto变量产生强引用
2.如果block被拷贝到堆上
- 会调用block内部的copy函数
- copy函数内部会调用_Block_object_assign函数
- _Block_object_assign函数会根据auto变量的修饰符(__strong、__weak、* __unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用
3.如果block从堆上移除
- 会调用block内部的dispose函数
- dispose函数内部会调用_Block_object_dispose函数
- _Block_object_dispose函数会自动释放引用的auto变量(release)