面试题答案
一键面试使用Instruments工具排查
- Leaks工具:
- 功能:用于检测应用运行过程中的内存泄漏情况。它会实时监控应用的内存分配与释放,当发现有内存区域已经无法访问,但却未被释放时,就会将其标记为内存泄漏。
- 使用方法:在Xcode中,选择 “Product” -> “Profile”,然后在Instruments中选择 “Leaks” 模板。运行应用,Leaks工具会在发现内存泄漏时在时间轴上标记,展开泄漏事件可以查看泄漏对象的详细信息,如类名、分配地址、堆栈跟踪等,通过堆栈跟踪可以定位到内存分配的代码位置。
- Allocations工具:
- 功能:不仅能展示应用内存使用的总体情况,包括当前活动的字节数、总字节数等,还可以跟踪每个对象的内存分配和释放历史。通过观察对象的生命周期,判断对象是否在不再需要时没有被正确释放。
- 使用方法:同样在Instruments中选择 “Allocations” 模板。运行应用后,可以在 “Recorded Objects” 中筛选出特定类的对象,观察其数量变化以及内存占用情况。如果某个对象数量持续增长且没有合理的业务逻辑解释,可能存在内存泄漏。同时,通过 “Call Stack” 可以查看对象分配的代码路径。
- Zombies工具:
- 功能:用于检测过度释放对象导致的应用崩溃(僵尸对象问题)。当一个对象被释放后,又向其发送消息,就会产生僵尸对象错误。Zombies工具会将已释放的对象替换为特殊的 “僵尸对象”,当向这些僵尸对象发送消息时,工具会捕获并报告相关信息。
- 使用方法:在Instruments中选择 “Zombies” 模板。运行应用,当出现向僵尸对象发送消息的情况时,Zombies工具会弹出提示,显示发送消息的对象、消息内容以及调用堆栈,从而定位到引发问题的代码位置。
从代码逻辑方面入手分析
- 自定义视图相关:
- 视图生命周期管理:检查视图的
init
、dealloc
方法。在init
方法中确保正确初始化所有成员变量,避免内存未初始化的问题。在dealloc
方法中,要确保释放所有在视图生命周期内分配的资源,如解除对其他对象的强引用、取消注册的通知等。例如,如果视图在init
中注册了某个通知,在dealloc
中必须调用[[NSNotificationCenter defaultCenter] removeObserver:self name:notificationName object:nil]
来移除通知,否则会导致视图无法释放。 - 视图控制器与视图的关系:确认视图控制器对视图的引用是否合理。如果视图控制器持有视图的强引用,而视图又持有视图控制器的强引用(例如通过代理),就会形成循环引用导致内存泄漏。可以将视图对视图控制器的引用改为弱引用(在Objective-C中使用
__weak
修饰符)来打破循环。 - 视图的添加与移除:当视图从父视图中移除时,要确保相关的资源也被正确清理。例如,如果视图中有正在运行的动画,在移除视图时要停止动画,避免动画相关的对象无法释放。
- 视图生命周期管理:检查视图的
- 网络请求相关:
- 请求任务管理:检查网络请求任务的创建与释放。如果使用
NSURLSession
进行网络请求,确保在请求完成后(无论是成功还是失败),正确处理请求任务。例如,对于基于NSURLSessionDataTask
的请求,在请求完成回调中,要取消任务(调用[task cancel]
),并且释放任务对象持有的任何资源。如果没有正确取消任务,任务可能在后台持续运行,占用内存。 - 响应数据处理:当接收到网络响应数据时,要注意数据的存储与释放。如果将响应数据存储在类的成员变量中,确保在不需要这些数据时及时释放。例如,如果将大的JSON响应数据存储在
NSDictionary
中,在处理完数据后,要将该NSDictionary
变量置为nil
,以便ARC(自动引用计数)可以释放其占用的内存。 - 网络请求回调中的引用关系:在网络请求的回调块中,要注意避免形成循环引用。如果回调块中持有视图控制器或其他对象的强引用,而这些对象又持有回调块,就会导致循环引用。可以使用
__weak
修饰符来捕获视图控制器等对象,例如__weak typeof(self) weakSelf = self; completionBlock(^{ __strong typeof(weakSelf) strongSelf = weakSelf; // 使用strongSelf处理业务逻辑 });
,这样在回调执行期间,即使视图控制器被释放,也不会导致内存泄漏。
- 请求任务管理:检查网络请求任务的创建与释放。如果使用
- 数据缓存操作相关:
- 缓存策略:审查缓存的设置与清理策略。如果缓存是无限增长的,例如使用一个
NSMutableDictionary
作为缓存,并且不断向其中添加数据而不进行清理,会导致内存占用不断上升。可以设置缓存的最大容量,当缓存达到上限时,根据一定的算法(如LRU - 最近最少使用算法)移除旧的数据。 - 缓存对象的生命周期:确保缓存对象的生命周期与应用的需求相匹配。如果缓存的对象已经不再被应用使用,但仍然存在于缓存中,就会造成内存浪费。例如,对于图片缓存,当图片对应的视图已经从屏幕上移除,并且短期内不会再次使用时,应该从缓存中移除该图片数据。
- 缓存与其他对象的引用关系:检查缓存与其他对象之间的引用关系,避免循环引用。如果缓存对象持有其他对象的强引用,而这些对象又依赖于缓存对象,可能导致内存泄漏。可以通过合理设置引用类型(如使用弱引用)来解决这个问题。
- 缓存策略:审查缓存的设置与清理策略。如果缓存是无限增长的,例如使用一个