iOS内存泄漏

iOS开发中,内存泄漏的问题常常遇到. 轻则影响性能,重则导致crash.那么,有哪些方法可以有效的检测内存泄漏的问题呢. 这里做一个总结

Analyze

Analyze是静态分析工具,可以通过 Product ->Analyze打开. Analyze主要分析以下四种问题:

  • 逻辑错误:访问空指针或未初始化的变量等
  • 内存管理错误:如内存泄漏等
  • 声明错误:从未使用过的变量;
  • Api调用错误:未包含使用的库和框架。

打开Analyze完成分析后,会出现如下图中所示的蓝色箭头. 箭头标注的地方,就是疑似发生内存泄漏的地方. 并且给出提示.


iOS内存泄漏_第1张图片
image.png

Instruments-Leaks

Leaks可以动态的检测程序运行中出现的内存泄漏的问题.
通过Product ->Profile->Leaks打开leaks.程序运行后,如果出现如下图中所示的红点,则表示发生了内存泄漏.


iOS内存泄漏_第2张图片
image.png

然后选择Cycles&Roots,如下图,可以看到循环引用.


iOS内存泄漏_第3张图片
image.png

选择CallTree,如下图,列表中为发生内存泄漏地方.
iOS内存泄漏_第4张图片
image.png

双击其中的某调,会显示出发生内存泄漏的代码.
iOS内存泄漏_第5张图片
image.png

大部分的内存泄漏问题就都能够发现了.

MLeaksFinder

MLeaksFinder是腾讯开源出的一个监测内存泄漏的工具.

具体的方法是,为基类 NSObject 添加一个方法 -willDealloc 方法,该方法的作用是,先用一个弱指针指向 self,并在一小段时间(3秒)后,通过这个弱指针调用 -assertNotDealloc,而 -assertNotDealloc 主要作用是直接中断言。

- (BOOL)willDealloc {
    NSString *className = NSStringFromClass([self class]);
    if ([[NSObject classNamesWhitelist] containsObject:className])
        return NO;
    NSNumber *senderPtr = objc_getAssociatedObject([UIApplication sharedApplication], kLatestSenderKey);
    if ([senderPtr isEqualToNumber:@((uintptr_t)self)])
        return NO;
    
    __weak id weakSelf = self;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        __strong id strongSelf = weakSelf;
        [strongSelf assertNotDealloc];
    });
    
    return YES;
}

- (void)assertNotDealloc {
    if ([MLeakedObjectProxy isAnyObjectLeakedAtPtrs:[self parentPtrs]]) {
        return;
    }
    [MLeakedObjectProxy addLeakedObject:self];
    
    NSString *className = NSStringFromClass([self class]);
    NSLog(@"Possibly Memory Leak.\nIn case that %@ should not be dealloced, override -willDealloc in %@ by returning NO.\nView-ViewController stack: %@", className, className, [self viewStack]);
}

使用MLeaksFinder 并不需要手动的去调用willDealloc方法, MLeaksFinder使用了runtime技术以及AOP思想,做到了对象的自动检测.
MLeaksFinder 一开始从 UIViewController 入手。当一个 UIViewController 被 pop 或 dismiss 后,该 UIViewController 包括它的 view,view 的 subviews 等等将很快被释放(除非你把它设计成单例,或者持有它的强引用,但一般很少这样做)。于是,我们只需在一个 ViewController 被 pop 或 dismiss 一小段时间后,看看该 UIViewController,它的 view,view 的 subviews 等等是否还存在。
具体的代码如下:


+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [self swizzleSEL:@selector(viewDidDisappear:) withSEL:@selector(swizzled_viewDidDisappear:)];
        [self swizzleSEL:@selector(viewWillAppear:) withSEL:@selector(swizzled_viewWillAppear:)];
        [self swizzleSEL:@selector(dismissViewControllerAnimated:completion:) withSEL:@selector(swizzled_dismissViewControllerAnimated:completion:)];
    });
}

- (void)swizzled_viewDidDisappear:(BOOL)animated {
    [self swizzled_viewDidDisappear:animated];
    
    if ([objc_getAssociatedObject(self, kHasBeenPoppedKey) boolValue]) {
        [self willDealloc];
    }
}

- (void)swizzled_viewWillAppear:(BOOL)animated {
    [self swizzled_viewWillAppear:animated];
    
    objc_setAssociatedObject(self, kHasBeenPoppedKey, @(NO), OBJC_ASSOCIATION_RETAIN);
}

- (void)swizzled_dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion {
    [self swizzled_dismissViewControllerAnimated:flag completion:completion];
    
    UIViewController *dismissedViewController = self.presentedViewController;
    if (!dismissedViewController && self.presentingViewController) {
        dismissedViewController = self;
    }
    
    if (!dismissedViewController) return;
    
    [dismissedViewController willDealloc];
}

- (BOOL)willDealloc {
    if (![super willDealloc]) {
        return NO;
    }
    
    [self willReleaseChildren:self.childViewControllers];
    [self willReleaseChild:self.presentedViewController];
    
    if (self.isViewLoaded) {
        [self willReleaseChild:self.view];
    }
    
    return YES;
}

首先在load方法中,对UIViewController lifeCycle中会调用到的方法,进行方法互换. 在调用相应方法的时候,插入想要执行的代码. 达到自动执行willDealloc的目的.

你可能感兴趣的:(iOS内存泄漏)