1、观察效果
在MRC环境下观察以下代码:
int main(int argc, const char * argv[]) {@autoreleasepool {Person *person = [[Person alloc] init];[person release];NSLog(@"mark");}return 0;}
打印结果:
~: -[Person dealloc]~: markProgram 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]~: markProgram 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对象,所以在代码执行结束后会自动销毁。
