美文网首页iOS
Block的循环引用

Block的循环引用

作者: YY_Lee | 来源:发表于2019-02-12 16:03 被阅读8次

    先上代码

    
    #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。

    相关文章

      网友评论

        本文标题:Block的循环引用

        本文链接:https://www.haomeiwen.com/subject/aavheqtx.html