美文网首页
循环引用导致内存泄漏

循环引用导致内存泄漏

作者: iOS俱哥 | 来源:发表于2019-04-10 09:46 被阅读0次

    前言

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

    接下来就用一个小例子来聊聊循环引用。

    案例

    在controller中有:

    [ZJHttpManager requestName:^(NSString * _Nonnull name) {
    
          self.name = name;
            
          NSLog(@"name is %@",name);
            
    }];
    

    模拟数据请求过程。如果在ZJHttpManager的requestName:方法中没有对block引用,也就没有对self进行引用,在释放controller时是会执行dealloc的。

    //ZJHttpManager.h

    @interface ZJHttpManager : NSObject
    
    + (void)requestName:(void(^)(NSString *name))successBlock;
    
    @end
    

    //ZJHttpManager.m

    #import "ZJHttpManager.h"
    
    typedef void (^ MyBlock)(NSString *name);
    
    @interface ZJHttpManager()
    
    @property(nonatomic, strong,nullable) NSTimer * timer;
    
    @property(nonatomic, copy) MyBlock block;
    
    @end
    
    @implementation ZJHttpManager
    
    - (instancetype)init{
        if (self = [super init]) {
    
            self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(run) userInfo:nil repeats:YES];
            
            [[NSRunLoop currentRunLoop]addTimer:self.timer forMode:NSRunLoopCommonModes];
            
        }
        
        return self;
    }
    
    - (void)run{
        NSLog(@"___%@",NSStringFromSelector(_cmd));
    }
    
    + (void)requestName:(void(^)(NSString *name))successBlock{
        
        ZJHttpManager * manager = [[ZJHttpManager alloc]init];
        
        manager.block = successBlock;
    
        if (successBlock) {
    
            successBlock(@"小明");
        }
        
    }
    
    @end
    

    本示例中是用NSTimer来制造出循环引用,在开发过程中可能是由于其他情况造成的,需具体分析。
    timer在未释放时可以一直打印,也可从是否打印看出是否释放了timer。

    有内存泄漏 未dealloc

    [ZJHttpManager requestName:^(NSString * _Nonnull name) {
    
          self.name = name;
            
          NSLog(@"name is %@",name);
            
    }];
    

    当ZJHttpManager的block直接引用self的情况下,self是没有执行dealloc进行释放的。

    执行dealloc

    虽然执行了dealloc,释放了self了,但是未循环引用未打破,还是存在内存泄漏的隐患。接下来看具体分析。

    __weak typeof(self) weakSelf = self;
        
        [ZJHttpManager requestName:^(NSString * _Nonnull name) {
    
            weakSelf.name = name;
            
            NSLog(@"name is %@",name);
            
        }];
    

    self被weak弱化下,可以得出结论会执行dealloc,self是可以被释放了,但是此时timer还一直在打印,并未得到释放。

    到此可以得出结论,ZJHttpManager类方法法中有一个循环引用,即manager对象引用timer,而timer的target是manager对象,也引用manager,造成一个循环体。这个循环体中的manager强引用着controller。所以在该释放controller的时候并未执行dealloc进行释放。

    此时弱化self(即当前controller),也仅仅是弱化了循环体对controller的引用,并未打破真正的循环体。仍会造成内存泄漏。

    正确处理方式

    应该在适当的地方真正打破循环引用。本案例中,应打破timer和manager的循环,在适当的地方释放timer。

    总结:在实际开发中,隐形的循环引用是导致内存泄漏的一个重要原因,可以在工程中集成查找内存泄漏的工具,进而发现内存泄漏的隐患。
    测试小demo

    相关文章

      网友评论

          本文标题:循环引用导致内存泄漏

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