MLeaksFinder 实践与源码解析:在开发阶段自动定位 iOS 内存泄漏

发布时间:2026/10/15 2:11:05
MLeaksFinder 实践与源码解析:在开发阶段自动定位 iOS 内存泄漏 开发工具质量保障【免费下载链接】MLeaksFinderFind memory leaks in your iOS app at develop time.项目地址https://gitcode.com/gh_mirrors/ml/MLeaksFinder点击查看免费下载MLeaksFinder 是腾讯开源的 iOS 内存泄漏检测工具其核心思路是在开发阶段Debug 构建自动监控 UIView 与 UIViewController 对象的生命周期一旦发现本该释放却仍存活的对象就弹出带完整 View-ViewController 调用栈的告警并支持扩展检测任意业务对象、可选集成 FBRetainCycleDetector 定位循环引用。阅读本文后你将掌握 MLeaksFinder 的接入方式、运行原理、屏蔽误报的方法以及如何用MLCheck扩展检测自己的业务对象。本文以仓库根目录的 README.md 为主线并结合MLeaksFinder/目录下的全部源码实现与 FAQ-CN.md 进行深度展开。官方还维护了一份中文 FAQ遇到接入问题时可对照排查。一、MLeaksFinder 是什么根据 README.md 的定位MLeaksFinder 帮助你在开发阶段develop time发现 iOS App 中的内存泄漏自动检测无需人工埋点自动发现 UIView 和 UIViewController 对象的泄漏直观告警泄漏发生时弹出 Alert 并在其中展示泄漏对象所在的 View-ViewController 栈一眼定位谁没被释放可扩展除 UIView / UIViewController 外开发者可以把检测范围扩展到任意类型的对象见下文扩展检测其他对象循环引用辅助可选用 Facebook 的 FBRetainCycleDetector 为泄漏对象尝试找出循环引用retain cycle。需要强调的是MLeaksFinder 的定位是开发期工具而非线上监控它的默认行为只在 Debug 构建下生效详见下文宏开关说明通过牺牲少量运行时代价换取开发时的即时反馈。二、快速接入一行 pod 搞定在项目的 Podfile 中加入pod MLeaksFinder然后执行pod install。根据 README.md 的说明MLeaksFinder 在pod install之后即自动生效不需要添加任何代码也不需要 import 任何头文件。这是它区别于传统手动调用检测 API类工具的最大特点——底层通过 Category Method Swizzling 在 App 启动时自动注入检测逻辑详见第三节。从仓库的 MLeaksFinder.podspec 可以确认以下事实Podspec 字段值说明s.nameMLeaksFinder组件名s.version1.0.0当前仓库版本号s.ios.deployment_target6.0最低支持 iOS 6.0s.source_filesMLeaksFinder/**/*打包目录为仓库内MLeaksFinder/全部源文件s.public_header_filesMLeaksFinder.h、NSObjectMemoryLeak.h对外暴露的公共头文件生效范围默认只在 Debug 生效在 MLeaksFinder.h 中可以看到生效开关的默认行为//#define MEMORY_LEAKS_FINDER_ENABLED 0 #ifdef MEMORY_LEAKS_FINDER_ENABLED #define _INTERNAL_MLF_ENABLED MEMORY_LEAKS_FINDER_ENABLED #else #define _INTERNAL_MLF_ENABLED DEBUG #endif即默认情况下_INTERNAL_MLF_ENABLED直接等于DEBUG宏——Debug 构建自动开启检测Release 构建自动关闭无需任何额外配置如果你需要手动控制可以取消MEMORY_LEAKS_FINDER_ENABLED的注释并显式指定 0 或 1。所有扩展类如UIViewControllerMemoryLeak.m的实现都包裹在#if _INTERNAL_MLF_ENABLED中关闭后相关代码整体不会参与编译。如何验证接入成功根据 FAQ-CN.md 的排查建议在 UIViewControllerMemoryLeak.m 的 (void)load方法里加一个断点App 启动时若能进入该方法说明 Category 注入成功否则说明接入失败使用 CocoaPods 安装时注意查看是否有 warnings特别是OTHER_LDFLAGS相关的 warning若有在主工程 Build Settings → Other Linker Flags 中加上-ObjC。提示 (void)load会在类首次加载时执行MLeaksFinder 正是利用它完成一系列 Method Swizzling 注入详见第四节。三、核心机制willDealloc 与延迟二次确认理解 MLeaksFinder 的原理只需要抓住一个关键设计它并不实时跟踪对象而是对应该被释放的对象做一个延迟复查。3.1 统一的检测入口willDealloc所有检测最终都收敛到NSObject分类里的- (BOOL)willDealloc方法见 NSObjectMemoryLeak.m。其执行逻辑如下- (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; }该方法按顺序做三件事白名单检查若对象所属类在白名单内直接返回NO不触发检测用于规避系统类的已知误报最新触摸对象检查若对象是最近一次用户触摸的 ViewkLatestSenderKey记录的指针也返回NO避免在触摸事件处理期间误判详见第四节延迟二次确认对self取一个__weak弱引用然后在主队列上dispatch_after2 秒检查弱引用是否还能取出强对象——这就是 MLeaksFinder 判定的关键如果对象在 2 秒内正常释放weakSelf为 nilassertNotDealloc不会执行一切安静如果 2 秒后对象依然存活说明它应该释放而没释放触发assertNotDealloc上报泄漏。这段逻辑对应的正是内存泄漏 本该释放却仍存活的定义。而本该释放的时机则由各 UIKit 扩展类在合适的生命周期钩子pop、disappear、dismiss 等处调用willDealloc来定义见第四节。3.2 上报与去重MLeakedObjectProxy一旦确认疑似泄漏assertNotDealloc见 NSObjectMemoryLeak.m会做两件事通过[MLeakedObjectProxy isAnyObjectLeakedAtPtrs:]检查该对象是否已上报过内部维护一个NSMutableSet *leakedObjectPtrs指针集合避免同一对象重复弹窗调用[MLeakedObjectProxy addLeakedObject:self]登记泄漏对象同时输出一条包含类名和 View-ViewController 栈的NSLog。MLeakedObjectProxy.m 中的addLeakedObject:在登记对象指针后通过 MLeaksMessenger.m 弹出标题为 Memory Leak 的 UIAlertView消息内容即该对象的viewStack。这里还有一个贴心设计代理对象以objc_setAssociatedObject关联在泄漏对象上一旦泄漏对象日后真的被释放MLeakedObjectProxy的dealloc会再弹一条 Object Deallocated 的提示告诉你那个疑似泄漏的对象后来被释放了方便反向印证是否为误报。3.3 View-ViewController 栈是如何生成的viewStack是贯穿整个机制的坐标系统。每个对象通过 Associated Object 挂载了kViewStackKey从 ViewController 到该对象的路径描述数组kParentPtrsKey父链路所有对象的指针集合用于去重判断。当检测一个对象时其子对象会在willReleaseObject:relationship:/willReleaseChild:/willReleaseChildren:中被逐级挂上栈见 NSObjectMemoryLeak.m。于是最终呈现的告警就是一条完整的从 ViewController 出发的视图链。四、自动检测的对象范围与注入点MLeaksFinder 默认覆盖 UIViewController、UIView 及常见的容器控制器注入点全部通过 (void)loadswizzleSEL:withSEL:定义在 NSObjectMemoryLeak.m完成。各扩展类的职责如下表扩展类注入点swizzle 的方法检测内容UIViewControllerMemoryLeak.mviewDidDisappear:、viewWillAppear:、dismissViewControllerAnimated:completion:控制器自身 childViewControllerspresentedViewControllerviewUIViewMemoryLeak.m无需 swizzle被 VC 触发subviews递归UINavigationControllerMemoryLeak.mpushViewController:animated:、popViewControllerAnimated:、popToViewController:animated:、popToRootViewControllerAnimated:viewControllersUIPageViewControllerMemoryLeak.m无viewControllersUISplitViewControllerMemoryLeak.m无viewControllersUITabBarControllerMemoryLeak.m无viewControllers4.1 UIViewController三个关键钩子以 UIViewControllerMemoryLeak.m 为例它 swizzle 了三个方法viewWillAppear:出现时把关联对象kHasBeenPoppedKey置为NO标记当前控制器还在栈上viewDidDisappear:若kHasBeenPoppedKey为YES即已被 pop则触发willDeallocdismissViewControllerAnimated:completion:dismiss 后对presentedViewController或自身当它作为被 present 的一方触发willDealloc。而- (BOOL)willDealloc见 UIViewControllerMemoryLeak.m在调用[super willDealloc]之后会继续向下释放childViewControllers、presentedViewController以及已加载的view——把检测递归扩散到整棵视图树。4.2 UINavigationController处理 pop 的时序细节UINavigationControllerMemoryLeak.m 是处理细节最多的扩展类popViewControllerAnimated:返回的控制器并不会立即释放尤其左边缘滑动返回时所以它把kHasBeenPoppedKey置为YES交给viewDidDisappear:阶段再触发检测popToViewController:/popToRootViewControllerAnimated:返回的控制器数组直接逐个触发willDealloc针对UISplitViewController的 Master-Detail 场景做了特殊处理Detail 控制器 pop 后不会立即释放而是暂存到kPoppedDetailVCKey等下一次 push 新 Detail 时再触发其检测。4.3 防误报UIApplication 与 UITouch 的最新触摸对象两个看似不相关的扩展类实际服务于误报过滤UIApplicationMemoryLeak.m swizzle 了sendAction:to:from:forEvent:每次事件分发时把sender指针记录为kLatestSenderKeyUITouchMemoryLeak.m swizzle 了setView:把触摸命中的 View 指针也记录为kLatestSenderKey。回到第三节的willDealloc如果待检测对象恰好是最近被触摸/最近参与事件的对象则跳过检测返回NO。这是为了避免对象因为正处于触摸响应链中被临时持有而产生的误报。五、阅读泄漏报告View-ViewController 栈README 给出了一个典型告警示例Memory Leak ( MyTableViewController, UITableView, UITableViewWrapperView, MyTableViewCell )这个栈的阅读规则是从栈顶开始前面的对象都已被确认成功释放栈底最后出现的那个对象就是疑似泄漏对象。在上例中MyTableViewController、UITableView、UITableViewWrapperView都已正常释放而MyTableViewCell没有——说明问题出在这条 cell 上例如 cell 被某个全局缓存、block 或代理强持有。告警同时会输出到控制台NSLog便于在 Xcode 中直接查看完整信息。六、Mute Assertion屏蔽不应释放的类如果某个类被设计为单例或者出于业务原因其对象本就不应该释放继续触发检测只会产生噪音。README 给出的标准做法是在业务类中重写willDealloc并返回NO- (BOOL)willDealloc { return NO; }注意任何子类在重写willDealloc时都应先调用[super willDealloc]当父类返回NO时直接透传NO详见第七节示例保证白名单、触摸对象过滤等父级判断依然生效。除运行时重写外框架本身还内置了一份类名白名单见 NSObjectMemoryLeak.mUIFieldEditorUIAlertController 的输入框UINavigationBar_UIAlertControllerActionView_UIVisualEffectBackdropViewUISwitch仅在 iOS 10.0 及以上加入源码注释标明这是 iOS 10 起至今未修复的系统 bug如果业务中有需要整类屏蔽的场景可以使用公开 API 动态追加 (void)addClassNamesToWhitelist:(NSArray *)classNames;例如[NSObject addClassNamesToWhitelist:[ MySingleton, MyCacheObject ]];七、扩展检测MLCheck 自定义对象MLeaksFinder 默认只检测 UIView / UIViewController 对象但 README 明确指出你可以把它扩展到以某个 UIViewController 为根的整个对象图。做法是在该控制器的willDealloc中对需要跟踪的业务对象调用MLCheck- (BOOL)willDealloc { if (![super willDealloc]) { return NO; } MLCheck(self.viewModel); return YES; }MLCheck是定义在 NSObjectMemoryLeak.h 的宏#define MLCheck(TARGET) [self willReleaseObject:(TARGET) relationship:#TARGET];其作用等价于调用willReleaseObject:relationship:#TARGET会把表达式原样字符串化作为关系描述例如传self.viewModel时 relationship 为self.viewModel。在 NSObjectMemoryLeak.m 的实现中若 relationship 以self开头会先去掉这个前缀避免栈里出现无意义的self.生成形如relationship(ClassName),的栈条目追加到子对象的viewStack把子对象指针加入parentPtrs集合调用子对象的willDealloc从而复用整套2 秒延迟二次确认机制。也就是说只要你的viewModel在控制器销毁后仍存活2 秒后就会收到带完整关系链的 Memory Leak 告警。检测 ViewModel、Model、网络层对象等任意 NSObject 子类都可以沿用同一模式。八、循环引用检测可选FBRetainCycleDetectorMLeaksFinder 曾集成 Facebook 的 FBRetainCycleDetector用于为泄漏对象尝试找出循环引用。但根据 README.md 中的WARNING由于 Facebook 的 BSD-plus-Patents 许可证问题FBRetainCycleDetector 已从 podspec 中移除MLeaksFinder.podspec 中对应的s.dependency也处于注释状态。如果仍想使用循环引用检测需要两步在你的项目 Podfile 中显式添加pod FBRetainCycleDetector在 MLeaksFinder.h 中开启宏#define MEMORY_LEAKS_FINDER_RETAIN_CYCLE_ENABLED 1开启后泄漏告警弹窗会多出一个 Retain Cycle 按钮见 MLeakedObjectProxy.m点击后代码会在后台线程用FBRetainCycleDetector以findRetainCyclesWithMaxCycleLength:20查找循环引用并将找到的环从泄漏对象处 shift 对齐后弹窗展示见 MLeakedObjectProxy.m。从源码结构看MLeaksFinder.h 中还保留了#elif COCOAPODS分支当MEMORY_LEAKS_FINDER_RETAIN_CYCLE_ENABLED宏未被定义、且工程经由 CocoaPods 集成COCOAPODS宏存在时内部开关_INTERNAL_MLF_RC_ENABLED会跟随COCOAPODS取值。仓库自带的示例 Podfile 与 Podfile.lock 中即包含FBRetainCycleDetector锁定版本 0.1.2说明官方示例工程默认启用了该能力。无论走哪条分支只要相关代码参与编译都必须保证 FBRetainCycleDetector 头文件可找到否则会编译失败。需要澄清的是Fail to find a retain cycle 并不代表没有泄漏。FAQ-CN.md 明确指出两种可能其一内存泄漏不一定是循环引用造成的比如被全局变量强持有其二有些循环引用 FBRetainCycleDetector 也未必能识别。因此该功能只是辅助手段找不到环不影响 MLeaksFinder 自身的泄漏判定。九、常见问题排查FAQ 精华综合 FAQ-CN.md 与源码接入过程中最常见的几个问题如下9.1 引进 MLeaksFinder 后没生效先验证引进是否正确在 UIViewControllerMemoryLeak.m 的 (void)load加断点App 启动能进入则引进成功检查 CocoaPods 安装时的 warnings尤其是OTHER_LDFLAGS相关有 warning 时在主工程 Build Settings → Other Linker Flags 加上-ObjC保证 Objective-C Category 被正确链接进可执行文件。9.2 可以手动引进吗可以。直接把MLeaksFinder/下的源码放入项目即可生效若以子工程方式引入同样需要在主工程 Other Linker Flags 加-ObjC。手动引进后泄漏检测即生效但循环引用功能还需额外加入 FBRetainCycleDetector 代码并打开 MLeaksFinder.h 中的宏开关。9.3 如何关闭 MLeaksFinder默认情况下它只在 Debug 构建下生效_INTERNAL_MLF_ENABLED取DEBUG见 MLeaksFinder.h。如需强制关闭取消注释并设置为 0#define MEMORY_LEAKS_FINDER_ENABLED 0Release 构建无需任何处理即不包含检测逻辑。十、仓库结构速览MLeaksFinder/ ├── MLeaksFinder.h # 总开关宏ENABLED / RETAIN_CYCLE_ENABLED ├── NSObjectMemoryLeak.{h,m} # 核心willDealloc、MLCheck、白名单、swizzle 工具 ├── UIViewControllerMemoryLeak.m # VC 生命周期钩子注入 ├── UIViewMemoryLeak.m # subviews 递归检测 ├── UINavigationControllerMemoryLeak.m ├── UIPageViewControllerMemoryLeak.m ├── UISplitViewControllerMemoryLeak.m ├── UITabBarControllerMemoryLeak.m ├── UIApplicationMemoryLeak.m # 防误报记录最新事件 sender ├── UITouchMemoryLeak.m # 防误报记录最新触摸 View ├── MLeakedObjectProxy.{h,m} # 泄漏登记、去重、循环引用按钮处理 ├── MLeaksMessenger.{h,m} # UIAlertView 弹出与 NSLog 输出 ├── MLeaksFinder.xcodeproj/ # 静态库工程产物 libMLeaksFinder.a ├── MLeaksFinder.podspec # CocoaPods 描述v1.0.0iOS 6.0 ├── Podfile / Podfile.lock # 示例工程含 FBRetainCycleDetector 0.1.2 ├── README.md # 官方说明本文主线 └── FAQ-CN.md # 中文 FAQ排查指南整体来看MLeaksFinder 的设计非常轻量核心文件只有NSObjectMemoryLeak、MLeakedObjectProxy、MLeaksMessenger三份其余 UIKit 扩展类全部是生命周期钩子 子对象扩散的薄封装。对希望深入理解 Objective-C 运行时Method Swizzling、Associated Object和 iOS 内存泄漏排查的开发者来说这份源码是极佳的学习样本。结语MLeaksFinder 用延迟二次确认 对象图递归 栈式告警这套组合拳把内存泄漏检测变成了开发期的自动化流程接入成本接近于零告警信息直接指向泄漏对象在视图层级中的位置误报可通过willDealloc重写或白名单收敛检测范围可通过MLCheck自由扩展到业务对象。无论你是想直接把它引入工程作为日常排查工具还是希望借鉴其 Method Swizzling 与弱引用复查的思路都可以在本文基础上继续阅读 README.md、FAQ-CN.md 与MLeaksFinder/目录下的源码逐一印证。赞分享开发工具质量保障【免费下载链接】MLeaksFinderFind memory leaks in your iOS app at develop time.项目地址https://gitcode.com/gh_mirrors/ml/MLeaksFinder点击查看免费下载相关推荐MLeaksFinder进阶如何开发自定义的内存泄漏检测插件MLeaksFinder进阶如何开发自定义的内存泄漏检测插件 MLeaksFinder是iOS开发中强大的内存泄漏检测工具它能够在开发阶段自动发现UIVie开发工具质量保障iOS内存泄漏终极检测指南MLeaksFinder快速定位Swift项目中的内存问题iOS内存泄漏终极检测指南MLeaksFinder快速定位Swift项目中的内存问题 MLeaksFinder是iOS开发中一款强大的内存泄漏检测工具能够在开发工具质量保障MicroOrm.Dapper.Repositories扩展开发如何编写自定义SQL生成策略MicroOrm.Dapper.Repositories扩展开发如何编写自定义SQL生成策略 MicroOrm.Dapper.Repositories是一个上一篇CampusShame服务器维护GitHub Pages性能优化指南下一篇ConvNetJS 完整指南用纯 JavaScript 在浏览器与 Node.js 中定义、训练与编译神经网络创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询