MST
星途 面试题库

面试题:Objective-C编写的iOS应用中如何排查因内存管理不当引发的错误

在一个使用Objective-C开发的大型iOS应用中,频繁出现内存泄漏和应用崩溃的情况。已知应用中有大量的自定义视图、网络请求以及数据缓存操作。请详细说明你将如何系统性地排查因内存管理不当而引发的错误,包括但不限于使用哪些工具(如 Instruments 的具体功能),从哪些代码逻辑方面入手分析等。
21.0万 热度难度
编程语言Objective-C

知识考点

AI 面试

面试题答案

一键面试

使用Instruments工具排查

  1. Leaks工具
    • 功能:用于检测应用运行过程中的内存泄漏情况。它会实时监控应用的内存分配与释放,当发现有内存区域已经无法访问,但却未被释放时,就会将其标记为内存泄漏。
    • 使用方法:在Xcode中,选择 “Product” -> “Profile”,然后在Instruments中选择 “Leaks” 模板。运行应用,Leaks工具会在发现内存泄漏时在时间轴上标记,展开泄漏事件可以查看泄漏对象的详细信息,如类名、分配地址、堆栈跟踪等,通过堆栈跟踪可以定位到内存分配的代码位置。
  2. Allocations工具
    • 功能:不仅能展示应用内存使用的总体情况,包括当前活动的字节数、总字节数等,还可以跟踪每个对象的内存分配和释放历史。通过观察对象的生命周期,判断对象是否在不再需要时没有被正确释放。
    • 使用方法:同样在Instruments中选择 “Allocations” 模板。运行应用后,可以在 “Recorded Objects” 中筛选出特定类的对象,观察其数量变化以及内存占用情况。如果某个对象数量持续增长且没有合理的业务逻辑解释,可能存在内存泄漏。同时,通过 “Call Stack” 可以查看对象分配的代码路径。
  3. Zombies工具
    • 功能:用于检测过度释放对象导致的应用崩溃(僵尸对象问题)。当一个对象被释放后,又向其发送消息,就会产生僵尸对象错误。Zombies工具会将已释放的对象替换为特殊的 “僵尸对象”,当向这些僵尸对象发送消息时,工具会捕获并报告相关信息。
    • 使用方法:在Instruments中选择 “Zombies” 模板。运行应用,当出现向僵尸对象发送消息的情况时,Zombies工具会弹出提示,显示发送消息的对象、消息内容以及调用堆栈,从而定位到引发问题的代码位置。

从代码逻辑方面入手分析

  1. 自定义视图相关
    • 视图生命周期管理:检查视图的 initdealloc 方法。在 init 方法中确保正确初始化所有成员变量,避免内存未初始化的问题。在 dealloc 方法中,要确保释放所有在视图生命周期内分配的资源,如解除对其他对象的强引用、取消注册的通知等。例如,如果视图在 init 中注册了某个通知,在 dealloc 中必须调用 [[NSNotificationCenter defaultCenter] removeObserver:self name:notificationName object:nil] 来移除通知,否则会导致视图无法释放。
    • 视图控制器与视图的关系:确认视图控制器对视图的引用是否合理。如果视图控制器持有视图的强引用,而视图又持有视图控制器的强引用(例如通过代理),就会形成循环引用导致内存泄漏。可以将视图对视图控制器的引用改为弱引用(在Objective-C中使用 __weak 修饰符)来打破循环。
    • 视图的添加与移除:当视图从父视图中移除时,要确保相关的资源也被正确清理。例如,如果视图中有正在运行的动画,在移除视图时要停止动画,避免动画相关的对象无法释放。
  2. 网络请求相关
    • 请求任务管理:检查网络请求任务的创建与释放。如果使用 NSURLSession 进行网络请求,确保在请求完成后(无论是成功还是失败),正确处理请求任务。例如,对于基于 NSURLSessionDataTask 的请求,在请求完成回调中,要取消任务(调用 [task cancel]),并且释放任务对象持有的任何资源。如果没有正确取消任务,任务可能在后台持续运行,占用内存。
    • 响应数据处理:当接收到网络响应数据时,要注意数据的存储与释放。如果将响应数据存储在类的成员变量中,确保在不需要这些数据时及时释放。例如,如果将大的JSON响应数据存储在 NSDictionary 中,在处理完数据后,要将该 NSDictionary 变量置为 nil,以便ARC(自动引用计数)可以释放其占用的内存。
    • 网络请求回调中的引用关系:在网络请求的回调块中,要注意避免形成循环引用。如果回调块中持有视图控制器或其他对象的强引用,而这些对象又持有回调块,就会导致循环引用。可以使用 __weak 修饰符来捕获视图控制器等对象,例如 __weak typeof(self) weakSelf = self; completionBlock(^{ __strong typeof(weakSelf) strongSelf = weakSelf; // 使用strongSelf处理业务逻辑 });,这样在回调执行期间,即使视图控制器被释放,也不会导致内存泄漏。
  3. 数据缓存操作相关
    • 缓存策略:审查缓存的设置与清理策略。如果缓存是无限增长的,例如使用一个 NSMutableDictionary 作为缓存,并且不断向其中添加数据而不进行清理,会导致内存占用不断上升。可以设置缓存的最大容量,当缓存达到上限时,根据一定的算法(如LRU - 最近最少使用算法)移除旧的数据。
    • 缓存对象的生命周期:确保缓存对象的生命周期与应用的需求相匹配。如果缓存的对象已经不再被应用使用,但仍然存在于缓存中,就会造成内存浪费。例如,对于图片缓存,当图片对应的视图已经从屏幕上移除,并且短期内不会再次使用时,应该从缓存中移除该图片数据。
    • 缓存与其他对象的引用关系:检查缓存与其他对象之间的引用关系,避免循环引用。如果缓存对象持有其他对象的强引用,而这些对象又依赖于缓存对象,可能导致内存泄漏。可以通过合理设置引用类型(如使用弱引用)来解决这个问题。