iOS

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。

上一篇下一篇

猜你喜欢

热点阅读