iOS开发中,内存泄漏的问题常常遇到. 轻则影响性能,重则导致crash.那么,有哪些方法可以有效的检测内存泄漏的问题呢. 这里做一个总结
Analyze
Analyze是静态分析工具,可以通过 Product ->Analyze打开. Analyze主要分析以下四种问题:
- 逻辑错误:访问空指针或未初始化的变量等
- 内存管理错误:如内存泄漏等
- 声明错误:从未使用过的变量;
- Api调用错误:未包含使用的库和框架。
打开Analyze完成分析后,会出现如下图中所示的蓝色箭头. 箭头标注的地方,就是疑似发生内存泄漏的地方. 并且给出提示.
Instruments-Leaks
Leaks可以动态的检测程序运行中出现的内存泄漏的问题.
通过Product ->Profile->Leaks打开leaks.程序运行后,如果出现如下图中所示的红点,则表示发生了内存泄漏.
然后选择Cycles&Roots,如下图,可以看到循环引用.
选择CallTree,如下图,列表中为发生内存泄漏地方.
双击其中的某调,会显示出发生内存泄漏的代码.
大部分的内存泄漏问题就都能够发现了.
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的目的.