1、观察效果
在MRC环境下观察以下代码:
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [[Person alloc] init];
[person release];
NSLog(@"mark");
}
return 0;
}
打印结果:
~: -[Person dealloc]
~: mark
Program ended with exit code: 0
当person对象调用release之后会自动销毁。再观察下面代码:
typedef void(^XLBlock)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
XLBlock block;
Person *person = [[Person alloc] init];
block = ^{
NSLog(@"person = %@", person);
};
block();
NSLog(@"block = %@", block);
[person release];
NSLog(@"mark");
[block release];
}
return 0;
}
打印结果:
~: person = <Person: 0x100670df0>
~: block = <__NSStackBlock__: 0x7ffeefbff418>
~: -[Person dealloc]
~: mark
Program ended with exit code: 0
当block是Stack类型时,访问person对象,person在调用release方法时可以正常销毁,再观察以下代码:
typedef void(^XLBlock)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
XLBlock block;
Person *person = [[Person alloc] init];
block = [^{
NSLog(@"person = %@", person);
} copy];
block();
NSLog(@"block = %@", block);
[person release];
NSLog(@"mark");
[block release];
}
return 0;
}
打印结果:
~: person = <Person: 0x105b12900>
~: block = <__NSMallocBlock__: 0x105b2cb30>
~: mark
~: -[Person dealloc]
Program ended with exit code: 0
当block调用了copy编程malloc类型后,person调用release方法后没有立即释放,等到block调用release方法后才释放。可以总结出:当block在栈上时不会引用对象类型的auto变量,当block被复制到堆上时,会引用对象类型的auto变量。
在ARC环境下观察如下代码:
int main(int argc, const char * argv[]) {
@autoreleasepool {
XLBlock block;
{
Person *person = [[Person alloc] init];
block = ^{
NSLog(@"person = %@", person);
};
block();
NSLog(@"block = %@", block);
}
NSLog(@"mark");
}
return 0;
}
*ARC环境下,block访问auto变量,block会被复制到堆上。
打印结果:
~: person = <Person: 0x105035910>
~: block = <__NSMallocBlock__: 0x105043350>
~: mark
~: -[Person dealloc]
Program ended with exit code: 0
在person离开作用域时,没有马上释放,而是在block离开作用域被释放时,person才被释放。再观察如下代码:
int main(int argc, const char * argv[]) {
@autoreleasepool {
XLBlock block;
{
Person *person = [[Person alloc] init];
__weak typeof(person)weakPerson = person;
block = ^{
NSLog(@"weakPerson = %@", weakPerson);
};
block();
NSLog(@"block = %@", block);
}
NSLog(@"mark");
}
return 0;
}
打印结果:
~: weakPerson = <Person: 0x10063a3c0>
~: block = <__NSMallocBlock__: 0x10064eeb0>
~: -[Person dealloc]
~: mark
在person离开作用域时,马上被释放了。可以总结出block对对象类型的auto变量强引用还是弱引用取决于访问的对象变量的引用方式。
2、底层探究
新建一个Person类继承于NSObject,在block内访问person对象
Person *person = [[Person alloc] init];
void (^block)(void) = ^{
NSLog(@"person = %p",person);
};
block();
转成C++代码后查看:
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
Person *person;
};
block结构体内有一个person对象的指针,且__main_block_desc_0结构体里会比访问基础类型auto变量多处了两个方法,copy和dispose:
// desc结构体,多了copy和depose方法
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
}
copy方法:
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
_Block_object_assign((void*)&dst->person, (void*)src->person, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
当block执行copy方法时,内部的main_block_copy_0方法会根据捕获auto变量的引用类型(strong、weak)进行引用。(比如外部person用weak修饰,那么内部block也将弱引用person)
dispose方法:
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
_Block_object_dispose((void*)src->person, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
当block从堆上移除时,会调用内部的__main_block_dispose_0方法,释放内部捕获的对象类型auto变量,相当于release。
3、总结
当block内部访问了对象类型的auto变量时:
如果block是在栈上,将不会对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)。
4、应用举例
创建一个Person类,添加test方法,重写Person的dealloc方法。
1、创建一个Person对象,使用GCD延时调用person的test方法:
{
NSLog(@"start");
Person *person = [[Person alloc] init];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[person test];
});
}
打印结果:
~: start
/** 3秒后 **/
~: person dealloc
person会在3秒后销毁,原因是person对象被block强引用,等block销毁时,执行dispose方法后才会释放person。
2、在block内部访问弱引用的person对象:
{
NSLog(@"start");
Person *person = [[Person alloc] init];
__weak Person* weakPerson = person;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[weakPerson test];
});
}
打印结果:
~: start
~: person dealloc
person对象会被马上销毁,因为block内部也弱引用了person对象,所以在代码执行结束后会自动销毁。