美文网首页牛叉的demo
MLeaksFinder 分析4:深入细节

MLeaksFinder 分析4:深入细节

作者: dc630f46ee2d | 来源:发表于2017-10-21 16:29 被阅读15次

    前言

    MLeaksFinder中,大多数文件只是系统类的category,使用Method Swizzling,提供了一个发送泄漏检查的时机。实际上真正检查内存泄漏的两个类是MLeakedObjectProxyMLeaksMessenger。前者负责数据逻辑,后者负责UI。

    MLeakedObjectProxy

    MLeakedObjectProxy对外有两个方法

    + (BOOL)isAnyObjectLeakedAtPtrs:(NSSet *)ptrs;
    + (void)addLeakedObject:(id)object;
    

    第一个方法用来判断ptrsNSSet类型)中是否有泄漏的对象,如果有返回True
    第二个方法是将对象加入泄漏对象的集合,同时调用MLeaksMessenger的弹窗方法
    无论是判断还是比较,始终需要一个集合来保存所有泄漏对象。自然而然检查MLeakedObjectProxy
    看到一个全局static变量static NSMutableSet *leakedObjectPtrs;,它就是用来做比较的对象。

    上面两个方法都只在 NSObject的category的assertNotDealloc中调用

    - (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]);
    }
    

    显然方法二的调用前提是方法一为True。初始化状态下,leakedObjectPtrs中没有元素,[MLeakedObjectProxy isAnyObjectLeakedAtPtrs:[self parentPtrs]]为NO,执行[MLeakedObjectProxy addLeakedObject:self]

    + (void)addLeakedObject:(id)object {
        NSAssert([NSThread isMainThread], @"Must be in main thread.");
        
        MLeakedObjectProxy *proxy = [[MLeakedObjectProxy alloc] init];
        proxy.object = object;
        proxy.objectPtr = @((uintptr_t)object);
        proxy.viewStack = [object viewStack];
        static const void *const kLeakedObjectProxyKey = &kLeakedObjectProxyKey;
        objc_setAssociatedObject(object, kLeakedObjectProxyKey, proxy, OBJC_ASSOCIATION_RETAIN);
        [leakedObjectPtrs addObject:proxy.objectPtr];
    }
    

    其中 [leakedObjectPtrs addObject:proxy.objectPtr]将自己封装成 MLeakedObjectProxy对象加入leakedObjectPtrs中。再回头看看isAnyObjectLeakedAtPtrs的实现

    + (BOOL)isAnyObjectLeakedAtPtrs:(NSSet *)ptrs {
        NSAssert([NSThread isMainThread], @"Must be in main thread.");
        
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            leakedObjectPtrs = [[NSMutableSet alloc] init];
        });
        
        if (!ptrs.count) {
            return NO;
        }
        if ([leakedObjectPtrs intersectsSet:ptrs]) {
            return YES;
        } else {
            return NO;
        }
    }
    

    会由于 [self ptrs]构成的 nssetleakedObjectPtrs对象有交集返回True,从而防止 addLeakedObject多次被调用,这里[self ptrs]的具体意义下一篇讲。

    MLeaksMessenger

    MLeaksMessenger 是一个工具类,用来弹内存泄漏的视图框。只有两个方法

    + (void)alertWithTitle:(NSString *)title message:(NSString *)message
     + (void)alertWithTitle:(NSString *)title
                   message:(NSString *)message
                  delegate:(id<UIAlertViewDelegate>)delegate
     additionalButtonTitle:(NSString *)additionalButtonTitle
    

    前者调用后者只是delegateadditionalButtonTitlenil.后者的实现如下

    static __weak UIAlertView *alertView;
    
    + (void)alertWithTitle:(NSString *)title
                   message:(NSString *)message
                  delegate:(id<UIAlertViewDelegate>)delegate
     additionalButtonTitle:(NSString *)additionalButtonTitle {
        [alertView dismissWithClickedButtonIndex:0 animated:NO];
        UIAlertView *alertViewTemp = [[UIAlertView alloc] initWithTitle:title
                                                                message:message
                                                               delegate:delegate
                                                      cancelButtonTitle:@"OK"
                                                      otherButtonTitles:additionalButtonTitle, nil];
        [alertViewTemp show];
        alertView = alertViewTemp;
        
        NSLog(@"%@: %@", title, message);
    } 
    

    这里有一个小技巧,使用静态全局变量用__weak修饰变量。第一次调用这个方法的时候,alertViewnil, [alertView dismissWithClickedButtonIndex:0 animated:NO]不产生任何操作,只是一个弹框。再次调用这个方法,会通过alertView来dimiss现有的弹框,再显示新的弹框。所以alertView是记录当前显示的内存泄漏的弹框。同时设置__weak修饰让这个全局变量弱引用。一旦弹框消失,自动设置为nil.

    相关文章

      网友评论

        本文标题:MLeaksFinder 分析4:深入细节

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