7
7
//
8
8
9
9
#import " NSObject+MemoryLeak.h"
10
+ #import " MLeakedObjectProxy.h"
10
11
#import " MLeaksFinder.h"
11
12
#import < objc/runtime.h>
12
13
#import < UIKit/UIKit.h>
13
14
14
15
static const void *const kViewStackKey = &kViewStackKey ;
16
+ static const void *const kParentPtrsKey = &kParentPtrsKey ;
15
17
const void *const kLatestSenderKey = &kLatestSenderKey ;
16
18
17
19
@implementation NSObject (MemoryLeak)
@@ -27,29 +29,60 @@ - (BOOL)willDealloc {
27
29
28
30
__weak id weakSelf = self;
29
31
dispatch_after (dispatch_time (DISPATCH_TIME_NOW, (int64_t )(3 * NSEC_PER_SEC)), dispatch_get_main_queue (), ^{
30
- [weakSelf assertNotDealloc ];
32
+ __strong id strongSelf = weakSelf;
33
+ [strongSelf assertNotDealloc ];
31
34
});
32
35
33
36
return YES ;
34
37
}
35
38
39
+ - (void )assertNotDealloc {
40
+ if ([MLeakedObjectProxy isAnyObjectLeakedAtPtrs: [self parentPtrs ]]) {
41
+ return ;
42
+ }
43
+ [MLeakedObjectProxy addLeakedObject: self ];
44
+
45
+ NSString *className = NSStringFromClass ([self class ]);
46
+ NSString *message = [NSString stringWithFormat: @" Possibly Memory Leak.\n In case that %@ should not be dealloced, override -willDealloc in %@ by returning NO.\n View-ViewController stack: %@ " , className, className, [self viewStack ]];
47
+ NSLog (@" %@ " , message);
48
+
49
+ UIAlertView *alertView = [[UIAlertView alloc ] initWithTitle: @" Memory Leak"
50
+ message: [NSString stringWithFormat: @" %@ " , [self viewStack ]]
51
+ delegate: nil
52
+ cancelButtonTitle: @" OK"
53
+ otherButtonTitles: nil ];
54
+ [alertView show ];
55
+ }
56
+
36
57
- (void )willReleaseObject : (id )object relationship : (NSString *)relationship {
37
58
if ([relationship hasPrefix: @" self" ]) {
38
59
relationship = [relationship stringByReplacingCharactersInRange: NSMakeRange (0 , 4 ) withString: @" " ];
39
60
}
40
61
NSString *className = NSStringFromClass ([object class ]);
41
62
className = [NSString stringWithFormat: @" %@ (%@ ), " , relationship, className];
42
63
43
- NSArray *viewStack = [ self viewStack ];
44
- [object setViewStack: [viewStack arrayByAddingObject: className ]];
64
+ [object setViewStack: [[ self viewStack ] arrayByAddingObject: className] ];
65
+ [object setParentPtrs: [[ self parentPtrs ] setByAddingObject: @(( uintptr_t ) self ) ]];
45
66
[object willDealloc ];
46
67
}
47
68
48
- - (void )assertNotDealloc {
49
- NSString *className = NSStringFromClass ([self class ]);
50
- NSString *message = [NSString stringWithFormat: @" Possibly Memory Leak.\n In case that %@ should not be dealloced, override -willDealloc in %@ by returning NO.\n View-ViewController stack: %@ " , className, className, [self viewStack ]];
51
- NSLog (@" %@ " , message);
52
- NSAssert (NO , message);
69
+ - (void )willReleaseChild : (id )child {
70
+ if (!child) {
71
+ return ;
72
+ }
73
+
74
+ [self willReleaseChildren: @[ child ]];
75
+ }
76
+
77
+ - (void )willReleaseChildren : (NSArray *)children {
78
+ NSArray *viewStack = [self viewStack ];
79
+ NSSet *parentPtrs = [[self parentPtrs ] setByAddingObject: @((uintptr_t )self )];
80
+ for (id child in children) {
81
+ NSString *className = NSStringFromClass ([child class ]);
82
+ [child setViewStack: [viewStack arrayByAddingObject: className]];
83
+ [child setParentPtrs: parentPtrs];
84
+ [child willDealloc ];
85
+ }
53
86
}
54
87
55
88
- (NSArray *)viewStack {
@@ -66,6 +99,18 @@ - (void)setViewStack:(NSArray *)viewStack {
66
99
objc_setAssociatedObject (self, kViewStackKey , viewStack, OBJC_ASSOCIATION_COPY );
67
100
}
68
101
102
+ - (NSSet *)parentPtrs {
103
+ NSSet *parentPtrs = objc_getAssociatedObject (self, kParentPtrsKey );
104
+ if (!parentPtrs) {
105
+ parentPtrs = [[NSSet alloc ] init ];
106
+ }
107
+ return parentPtrs;
108
+ }
109
+
110
+ - (void )setParentPtrs : (NSSet *)parentPtrs {
111
+ objc_setAssociatedObject (self, kParentPtrsKey , parentPtrs, OBJC_ASSOCIATION_RETAIN );
112
+ }
113
+
69
114
+ (NSSet *)classNamesInWhiteList {
70
115
static NSSet *whiteList;
71
116
static dispatch_once_t onceToken;
0 commit comments