MLeaksFinder简单实现原理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/dolacmeng/article/details/80822272

MLeaksFinder是 iOS 平台的自动内存泄漏检测工具,下面以demo来实现检测视图控制器是否内存泄漏,实现类似的功能,简单地了解MLeaksFinder的原理。

总体思路:在视图控制器弹出栈 && 视图完全消失时,监听对象是否已被正常销毁

1.新建NSObject分类,提供简便的方法交换类方法:

#import "NSObject+JXRuntime.h"
#import<objc/runtime.h>

@implementation NSObject (JXRuntime)

+(void)swizzleSEL:(SEL)originalSEL withSEL:(SEL)swizzledSEL{

    Class class = [self class];
    Method originalMethod = class_getInstanceMethod(class, originalSEL);
    Method swizzledMethod = class_getInstanceMethod(class, swizzledSEL);

    BOOL didAddMethod = class_addMethod(class, originalSEL, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));

    //若类没有实现需要被替换的原方法,也就是上一步添加原方法成功,此时已将本来要swizzle的方法的实现直接复制进原方法里。
    //还需要把新方法IMP指向原方法的实现:
    if (didAddMethod) {
        class_replaceMethod(class, swizzledSEL, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
    }
    //类有对应的原方法,则正常交换
    else{
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }

    //参考:https://www.jianshu.com/p/1bacd182329f
}

2.新建UIViewController分类,交换viewWillAppear:和viewDidDisappear:方法

#import "UIViewController+Leaks.h"
#import "NSObject+JXRuntime.h"
#import <objc/runtime.h>

const void *const kHasBeenPoppedKey = "kHasBeenPoppedKey";//标记是否已经出栈

@implementation UIViewController (Leaks)

+ (void)load{    
    //正常情况load只会执行一次,使用dispatch_once是为了防止人为调用load方法
    //在load方法而不是在initalize方法中进行交换的原因:(1)load在类被加载时调用,initalize在类或其子类收到第一条消息时才被调用,类似懒加载。(2)load方法在父类、子类、分类中是独立的
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [self swizzleSEL:@selector(viewWillAppear:) withSEL:@selector(swizzled_viewWillAppear:)];
        [self swizzleSEL:@selector(viewDidDisappear:) withSEL:@selector(swizzled_viewDidDisappear:)];
    });
}


- (void) swizzled_viewWillAppear:(BOOL) animated{
    [self swizzled_viewWillAppear:animated];

    //标记为进栈
    objc_setAssociatedObject(self, kHasBeenPoppedKey, @(NO), OBJC_ASSOCIATION_RETAIN);
}

-(void) swizzled_viewDidDisappear:(BOOL) animated{
    [self swizzled_viewDidDisappear:animated];

    //已经出栈
    if ([objc_getAssociatedObject(self, kHasBeenPoppedKey) boolValue] == YES) {
        //判断对象是否被销毁
        [self willDealloc];
    }
}

@end

3.新建UINavigationController分类,交换popViewControllerAnimated:方法。

#import "UINavigationController+Leaks.h"
#import <objc/runtime.h>
#import "NSObject+JXRuntime.h"

@implementation UINavigationController (Leaks)

+ (void)load{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [self swizzleSEL:@selector(popViewControllerAnimated:) withSEL:@selector(swizzled_popViewControllerAnimated:)];
    });
}

- (UIViewController*)swizzled_popViewControllerAnimated:(BOOL)animated{
    UIViewController *poppedViewController = [self swizzled_popViewControllerAnimated:animated];
    extern const void * const kHasBeenPoppedKey;
    //标记为已经出栈
    objc_setAssociatedObject(poppedViewController, kHasBeenPoppedKey, @(YES), OBJC_ASSOCIATION_RETAIN);
    return poppedViewController;
}

@end

4.新建UINavigationController分类,交换popViewControllerAnimated:方法。

#import "UINavigationController+Leaks.h"
#import <objc/runtime.h>
#import "NSObject+JXRuntime.h"

@implementation UINavigationController (Leaks)

+ (void)load{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [self swizzleSEL:@selector(popViewControllerAnimated:) withSEL:@selector(swizzled_popViewControllerAnimated:)];
    });
}

- (UIViewController*)swizzled_popViewControllerAnimated:(BOOL)animated{
    UIViewController *poppedViewController = [self swizzled_popViewControllerAnimated:animated];
    extern const void * const kHasBeenPoppedKey;
    //标记为已经出栈
    objc_setAssociatedObject(poppedViewController, kHasBeenPoppedKey, @(YES), OBJC_ASSOCIATION_RETAIN);
    return poppedViewController;
}

@end

完整demo:https://github.com/dolacmeng/LeakCheckDemo

猜你喜欢

转载自blog.csdn.net/dolacmeng/article/details/80822272