美文网首页
打破循环引用的正确姿势

打破循环引用的正确姿势

作者: SpursGo | 来源:发表于2016-12-06 11:54 被阅读99次

我们知道,oc中使用的是引用计数,哪么不可避免的就会产生两个对象互相引用对方,导致这两个对象都无法释放,产生内存泄漏。内存泄漏多了就回导致内存溢出,影响用户体验

使用weak来解决循环引用

1.让我们来看一下下面这段代码

//ClassB是一个UIViewController,假设从ClassA pushViewController将ClassB展示出来
@interface ClassB ()
@property (nonatomic, copy) dispatch_block_t block;
@property (nonatomic, strong) NSString *str;
@end
@implementation ClassB
- (void)dealloc {
}
- (void)viewDidLoad {
    [super viewDidLoad];
    self.str = @"111";
    __weak typeof(self) weakSelf = self;
    self.block = ^{
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"%@", weakSelf.str);
        });
    };
    self.block();   
}

这里会产生两种情况
1.若从A push到B,10s之内没有pop回A的话,B中block会执行打印出来111。
2.若从A push到B,10s之内pop回A的话,B会立即执行dealloc,从而导致B中block打印出(null)。这种情况就是使用weakSelf的缺陷,可能会导致内存提前回收。

使用weak & strong来解决上述问题

先上代码

@interface ClassB ()
@property (nonatomic, copy) dispatch_block_t block;
@property (nonatomic, strong) NSString *str;
@end
@implementation ClassB
- (void)dealloc {
}
- (void)viewDidLoad {
    [super viewDidLoad];
    self.str = @"111";
    __weak typeof(self) weakSelf = self;
    self.block = ^{
        __strong typeof(self) strongSelf = weakSelf;
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"%@", strongSelf.str);
        });
    };
    self.block();   
}

我们发现这样确实解决了问题,但是可能会有两个不理解的点。
1.这么做和直接用self有什么区别,为什么不会有循环引用:外部的weakSelf是为了打破环,从而使得没有循环引用,而内部的strongSelf仅仅是个局部变量,存在栈中,会在block执行结束后回收,不会再造成循环引用。
2.这么做和使用weakSelf有什么区别:唯一的区别就是多了一个strongSelf,而这里的strongSelf会使ClassB的对象引用计数+1,使得ClassB pop到A的时候,并不会执行dealloc,因为引用计数还不为0,strongSelf仍持有ClassB,而在block执行完,局部的strongSelf才会回收,此时ClassB dealloc。

这样做其实已经可以解决所有问题,但是强迫症的我们依然能找到它的缺陷:
1.block内部必须使用strongSelf,很麻烦,不如直接使用self简便。
2.很容易在block内部不小心使用了self,这样还是会引起循环引用,这种错误很难发觉。

终极解决方法

查看github上开源的libextobjc库,可以发现,里面的EXTScope.h里面有两个关于weak和strong的宏定义。

// 宏定义
#define weakify(...) \
    ext_keywordify \
    metamacro_foreach_cxt(ext_weakify_,, __weak, __VA_ARGS__)
#define strongify(...) \
    ext_keywordify \
    _Pragma("clang diagnostic push") \
    _Pragma("clang diagnostic ignored \"-Wshadow\"") \
    metamacro_foreach(ext_strongify_,, __VA_ARGS__) \
    _Pragma("clang diagnostic pop")

// 用法
@interface ClassB ()
@property (nonatomic, copy) dispatch_block_t block;
@property (nonatomic, strong) NSString *str;
@end
@implementation ClassB
- (void)dealloc {
}
- (void)viewDidLoad {
    [super viewDidLoad];
    self.str = @"111";
    @weakify(self)
    self.block = ^{
        @strongify(self)
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"%@", self.str);
        });
    };
    self.block();   
}

这样在ARC下基本上 block的循环引用问题就基本解决了,如果在MRC下你会发现这样做是不可以的 只能加上__block来解决循环引用问题

相关文章

  • 打破循环引用的正确姿势

    我们知道,oc中使用的是引用计数,哪么不可避免的就会产生两个对象互相引用对方,导致这两个对象都无法释放,产生内存泄...

  • Swift--内存管理

    Swift内存管理概述 强引用循环 打破强引用循环 闭包中的强引用循环 Swift内存管理概述 Swift中的AR...

  • iOS block 简单循环引用,大环引用(为什么用 __wea

    ios block 简单循环引用,大环引用(为什么用 __weak 修饰可以打破循环引用,怎样做断环操作) 分析下...

  • 解锁正确的缓存姿势

    引用地址:解锁正确的缓存姿势 springboot+redis引用:https://www.jianshu.com...

  • Python基础语法

    1、不在同一句import中引用多个库正确姿势:import osimport sys错误姿势:importos,...

  • 打破NSTimer循环引用的方案

    NSTimer相信大家经常都会用到,但是,如果不注意的话,很容易就会造成内存泄漏。我们先来暂时一个例子: 如果你从...

  • Swift5.1—隐式解包可选值属性

    上面弱引用和无主引用的例子涵盖了两种常用的需要打破循环强引用的场景。 Person 和 Apartment 的例子...

  • 循环引用导致内存泄漏

    前言 由循环引用导致的内存泄漏是常出现的一个原因。一般都是weak给弱化一方的指针,打破循环引用。一些隐藏的循环引...

  • 4-8 循环引用

    3种循环引用 自循环引用 相互循环引用 多循环引用 Block的循环引用 NSTimer 的循环引用 破除循环引用...

  • Swift之for循环

    前面学完了switch,接着来看看for循环。 下面的才是正确的姿势: 总结 以上就是简单的for循环的写法,比较简单。

网友评论

      本文标题:打破循环引用的正确姿势

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