Block的循环引用
2019-02-12 本文已影响8人
YY_Lee
先上代码
#import "ViewController.h"
#import "Person.h"
typedef void(^BlockDemo)(void);
@interface ViewController ()
@property(copy,nonatomic)BlockDemo block;
@property(strong,nonatomic)Person *person;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self circyleReference];
}
- (void)circyleReference {
self.person = [Person new];
self.person.name = @"yy";
self.block = ^{
NSLog(@"%@",self.person.name);
};
self.block();
}
上面的代码我们都知道会产生循环引用,ViewController持有block,block内部又强引用着ViewController,下面通过clang转换后的代码看看具体是怎样的:
struct ViewController_IMPL {
struct UIViewController_IMPL UIViewController_IVARS;
NSString *__strong _test;
__strong BlockDemo _block;
Person *__strong _person;
};
struct __ViewController__circyleReference_block_impl_0 {
struct __block_impl impl;
struct __ViewController__circyleReference_block_desc_0* Desc;
ViewController *const __strong self;
__ViewController__circyleReference_block_impl_0(void *fp, struct __ViewController__circyleReference_block_desc_0 *desc, ViewController *const __strong _self, int flags=0) : self(_self) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
可以看到ViewController内部有个强指针指向person对象,block内部有个强指针指向ViewController,即block和ViewController相互持有。
解决循环引用,一般将一端的强引用变成弱引用,因为block的调用时机是不固定的,所以最好让ViewController强引用着block,让block弱引用ViewController避免循环引用。在上一篇文章有提到ARC下block会根据外部的修饰符来对访问的变量强引用或者弱引用。我们可以通过以下修饰符实现block对ViewController的弱引用。
__weak: 不会产生强引用,指向的对象销毁后自动将指针置位nil
__unsafe_unretained:不会产生强引用,但指向的对象销毁后,指针存储地址值不变,不安全
通过代码具体看下,先看下__weak修饰符:
- (void)circyleReference {
self.person = [Person new];
/*
__weak: 不会产生强引用,指向的对象销毁后自动将指针置位nil
__unsafe_unretained:不会产生强引用,但指向的对象销毁后,指针存储地址值不变,不安全
*/
__weak ViewController *weakSelf = self;
self.person.name = @"yy";
self.block = ^{
NSLog(@"%@",weakSelf.person.name);
};
self.block();
}
//clang转换后的block
struct __ViewController__circyleReference_block_impl_0 {
struct __block_impl impl;
struct __ViewController__circyleReference_block_desc_0* Desc;
ViewController *__weak weakSelf; //弱引用
__ViewController__circyleReference_block_impl_0(void *fp, struct __ViewController__circyleReference_block_desc_0 *desc, ViewController *__weak _weakSelf, int flags=0) : weakSelf(_weakSelf) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
从代码中可以看到block对Viewcontroller是弱引用。
__unsafe_unretained修饰符:
- (void)circyleReference {
self.person = [Person new];
__unsafe_unretained ViewController *weakSelf = self;
self.person.name = @"yy";
self.block = ^{
NSLog(@"%@",weakSelf.person.name);
};
self.block();
}
//clang转换后的block
struct __ViewController__circyleReference_block_impl_0 {
struct __block_impl impl;
struct __ViewController__circyleReference_block_desc_0* Desc;
ViewController *__unsafe_unretained weakSelf;
__ViewController__circyleReference_block_impl_0(void *fp, struct __ViewController__circyleReference_block_desc_0 *desc, ViewController *__unsafe_unretained _weakSelf, int flags=0) : weakSelf(_weakSelf) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
从代码中可以看到block对Viewcontroller是弱引用。__unsafe_unretained不安全,所以一般解决循环引用用__weak。