实现 iOS 内存检测工具

在 iOS 开发中 内存泄漏是常见的问题, 这里整理下常用的内存检测方法.

一 静态检测方法

使用XCode分析功能,Product->Analyze

使用静态检测可以检查出一些明显的没有释放的内存,包括NSObject和CF开头的内存泄漏.

缺点: 不能检测block导致的内存泄漏问题


二 动态检测方法

使用 instruments

实现 iOS 内存检测工具_第1张图片


三 dealloc 重新检测

重写dealloc 方法, 在界面返回或者对象销毁的时候, 判断是否调用


四 第三方检测方法

MLLeaksFinder

主要检查UI方面的泄漏,集成简单,优点如下:
1.不同于instrument的分析功能,需要人工观察,这个工具自动检测,发现有泄漏后可以实时进行提示,虽然主要是针对UI,但对于一般的工程来说,内存泄漏的场景中还是以UI居多,因此可以解决很大部分的问题。
2.在新功能的开发过程和解决bug的过程中,出现内存泄漏都可以很轻松的检测出来。

其原理如下: 
(1) 检测viewController在pop后 是否已经释放
(2) 在viewWillAppear中将标志位设置NO
(3) 在Navgation pop后将标志位设置为YES
(4) 在viewDidDisappear 中判断标志位


我们根据这个原理, 简单实现一个自己简易的内存检测工具:

(1) 用runtime的技术交换 viewController 的 viewWillAppear 和 viewDidDisappear;

(2) 在viewWillAppear 中将标志位设为 NO, 代码如下:

- (void)sw_viewWillAppear:(BOOL)animated{
    [self sw_viewWillAppear:animated];
    
    objc_setAssociatedObject(self, &kLeakMemory, @(NO), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
(3) 交换 navigationController 的 popViewControllerAnimated, 并在其中将标志位设为YES
- (nullable UIViewController *)sw_popViewControllerAnimated:(BOOL)animated{
    UIViewController *vc = [self sw_popViewControllerAnimated:animated];
    
    extern const char *kLeakMemory;
    objc_setAssociatedObject(self, &kLeakMemory, @(YES), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    
    return vc;
}

(4) 在viewController的viewDidDisappear中判断标志位, 并延时检测dealloc是否调用

- (void)sw_viewDidDisappear:(BOOL)animated{
    [self sw_viewDidDisappear:animated];
    
    if (objc_getAssociatedObject(self, &kLeakMemory)) {
        [self willDealloc];
    }
}

- (BOOL)willDealloc {
    if (![super willDealloc]) {
        return NO;
    }
    return YES;
}
/**super willDealloc*/
- (BOOL)willDealloc{
    // 这里注意, 使用weakSelf 防止引起循环引用
    __weak typeof(self) weakSelf =  self;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        __strong typeof(weakSelf) strongSelf = weakSelf;
        [strongSelf showMsg:NSStringFromClass([strongSelf class])];
    });
    return YES;
}

- (void)showMsg:(NSString *)msg{
    UIAlertView *alertViewTemp = [[UIAlertView alloc] initWithTitle:@"Leak"
                                                            message:msg
                                                           delegate:nil
                                                  cancelButtonTitle:@"OK"
                                                  otherButtonTitles: nil];
    [alertViewTemp show];
}

Demo

如有疑问, 欢迎指出.

你可能感兴趣的:(iOS开发,内存管理)