iOS 源码分析(三):MLeaksFinder

2022-03-10  本文已影响0人  Style_月月

主要分析MLeaksFinder的原理和具体实现

Leaks

从苹果官方文档可知,一个app的内存主要分3类

介绍

MLeaksFinder 提供了内存泄露检测更好的解决方案。只需要引入 MLeaksFinder,就可以自动在 App 运行过程检测到内存泄露的对象并立即提醒,无需打开额外的工具,也无需为了检测内存泄露而一个个场景去重复地操作。MLeaksFinder 目前能自动检测 UIViewController 和 UIView 对象的内存泄露,而且也可以扩展以检测其它类型的对象。
当发生内存泄露时,MLeaksFinder 会中断言,并准确的告诉你哪个对象泄露了。这里设计为中断言而不是打日志让程序继续跑,是因为很多人不会去看日志,断言则能强制开发者注意到并去修改,而不是犯拖延症。

优势

从 MLeaksFinder 的使用方法可以看出,MLeaksFinder 具备以下优点:

原理

具体实现

//load方法中hook dissmiss方法
[self swizzleSEL:@selector(dismissViewControllerAnimated:completion:) withSEL:@selector(swizzled_dismissViewControllerAnimated:completion:)];

//swizzled_dismissViewControllerAnimated:completion:的具体实现
- (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;
   //视图控制器即将被释放时调用 willDealloc 方法
  [dismissedViewController willDealloc];
}

查看 willDealloc 的具体实现

- (BOOL)willDealloc {
  //调用父类的 willDealloc 方法
  if (![super willDealloc]) {
    return NO;
  }
  //构建堆栈信息
  [self willReleaseChildren:self.childViewControllers];
  [self willReleaseChild:self.presentedViewController];
   
  if (self.isViewLoaded) {
    [self willReleaseChild:self.view];
  }
   
  return YES;
}
- (BOOL)willDealloc {
  //获取类名
  NSString *className = NSStringFromClass([self class]);
  //判断这个class是否在白名单中,如果是,则忽略 -- 白名单机制
  if ([[NSObject classNamesWhitelist] containsObject:className])
    return NO;
  //UIControl的target-action机制,如果当前对象在发送action,则忽略(因为willDealloc会先于action调用)
  NSNumber *senderPtr = objc_getAssociatedObject([UIApplication sharedApplication], kLatestSenderKey);
  if ([senderPtr isEqualToNumber:@((uintptr_t)self)])
    return NO;
  //设置一个weak指针,指向self
  __weak id weakSelf = self;
  //在2s后,如果当前对象还没有释放,则会执行到assertNotDealloc方法。如果已经释放,那么weakSelf则会是nil,不会走到assertNotDealloc方法
  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)willReleaseChild:(id)child {
  if (!child) {
    return;
  }
   
  [self willReleaseChildren:@[ child ]];
}
//遍历子对象,然后将父对象的class name加上子对象的class name,一步步构造出一个 视图堆栈,如果出现内存泄漏,直接打印此对象的 视图堆栈即可
- (void)willReleaseChildren:(NSArray *)children {
  NSArray *viewStack = [self viewStack]; //通过关联对象获取值 子视图的 class name
  NSSet *parentPtrs = [self parentPtrs]; //通过关联对象获取 父对象的 class name
  for (id child in children) {
    NSString *className = NSStringFromClass([child class]);
    [child setViewStack:[viewStack arrayByAddingObject:className]];
    [child setParentPtrs:[parentPtrs setByAddingObject:@((uintptr_t)child)]];
    [child willDealloc];
  }
}

其整体的流程图示如下


整体的流程图示.jpg
上一篇 下一篇

猜你喜欢

热点阅读