在 iOS 开发中,使用 Objective-C 监控 ANR(Application Not Responding)和卡顿,主要依靠监控主线程的响应能力。由于 iOS 没有明确的 ANR 定义像 Android 那样,通常情况下是监控主线程的阻塞时间来判断应用是否卡顿或无响应。
以下是一些常用的卡顿/ANR 监控和防治手段:
一、监控 ANR/卡顿
-
RunLoop 监控:
这是最常用的方法。RunLoop 负责处理 App 中的各种事件(例如用户输入、网络请求、定时器等)。通过监控 RunLoop 的状态,可以判断主线程是否卡顿。
-
原理: 在主线程 RunLoop 的每个循环开始和结束时记录时间戳。如果两次时间戳的差值超过预设的阈值(例如 5 秒),则认为发生了卡顿。
-
代码示例:
#import "ANRMonitor.h" @interface ANRMonitor () @property (nonatomic, strong) CFRunLoopObserverRef observer; @property (nonatomic, assign) CFRunLoopActivity activities; @property (nonatomic, assign) NSTimeInterval lastRunLoopStartTime; @end @implementation ANRMonitor + (instancetype)sharedInstance { static ANRMonitor *instance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [[ANRMonitor alloc] init]; }); return instance; } - (void)startMonitoring { self.lastRunLoopStartTime = 0; CFRunLoopObserverContext context = {0, (__bridge void*)self, NULL, NULL, NULL}; self.observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, &runLoopObserverCallBack, &context); CFRunLoopAddObserver(CFRunLoopGetMain(), self.observer, kCFRunLoopCommonModes); } static void runLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) { ANRMonitor *monitor = (__bridge ANRMonitor*)info; monitor.activities = activity; if (activity == kCFRunLoopBeforeSources || activity == kCFRunLoopAfterWaiting) { monitor.lastRunLoopStartTime = CACurrentMediaTime(); } else if (activity == kCFRunLoopBeforeWaiting) { NSTimeInterval currentTime = CACurrentMediaTime(); NSTimeInterval runLoopDuration = currentTime - monitor.lastRunLoopStartTime; if (runLoopDuration > 5) { // 超过 5 秒视为卡顿 NSLog(@"Main thread stuck for %f seconds.", runLoopDuration); // 可以在这里收集卡顿信息,例如堆栈、CPU 使用率等 } } } - (void)stopMonitoring { if (self.observer) { CFRunLoopRemoveObserver(CFRunLoopGetMain(), self.observer, kCFRunLoopCommonModes); CFRelease(self.observer); self.observer = NULL; } } @end- 使用方法: 在
AppDelegate的application:didFinishLaunchingWithOptions:方法中调用[[ANRMonitor sharedInstance] startMonitoring];开始监控。
- 使用方法: 在
-
-
主线程 Ping:
创建一个子线程,定期向主线程发送消息。如果主线程在一定时间内没有响应消息,则认为主线程卡顿。
-
性能监控工具:
- Instruments: Xcode 自带的 Instruments 工具可以用来分析 CPU 使用率、内存分配、图形渲染等性能指标,帮助定位卡顿原因。
- 第三方性能监控平台: 像是 Sentry 3 和 AppDynamics [2, 5](docs.appdynamics.com/display/PRM…, docs.appdynamics.com/display/PRM…) 等,可以收集崩溃报告、性能数据,并提供可视化的分析工具。
二、卡顿防治手段
-
避免在主线程执行耗时操作:
- 将耗时任务(例如网络请求、图片加载、复杂计算)放到子线程执行,完成后再回到主线程更新 UI。可以使用 GCD 或 NSOperationQueue 来管理子线程。
-
优化 UI 渲染:
- 减少视图层级,使用 Auto Layout 优化布局计算。
- 避免离屏渲染,尽量使用不透明视图。
- 使用预加载和缓存机制,减少不必要的计算和绘制。
-
优化数据处理:
- 使用合适的数据结构和算法,避免不必要的遍历和查找。
- 使用缓存机制,减少重复计算。
-
代码优化:
- 避免循环引用,及时释放不再使用的对象。
- 避免使用过多的锁,减少线程竞争。
总结:
监控 ANR/卡顿需要结合多种手段,才能更全面地了解应用的性能状况。选择合适的监控方案和防治手段,可以有效提升应用的流畅度和用户体验。