在 Xcode 中排查线程泄露问题,可以使用以下多种方法:
1. 使用 Instruments 工具分析
System Trace 工具
# 启动方式:
Xcode → Product → Profile → Instruments → System Trace
分析步骤:
- 选择 System Trace 模板
- 录制应用运行过程
- 查看线程创建和销毁情况
- 重点关注:
- 线程数量随时间变化
- 线程生命周期
- 线程堆栈信息
Thread Sanitizer
# 启用方式:
Xcode → Scheme → Edit Scheme → Run → Diagnostics → Thread Sanitizer
功能特点:
- 实时检测线程创建和销毁
- 识别线程泄露模式
- 显示泄露线程的创建堆栈
2. Xcode 内置调试工具
Debug Navigator 线程监控
// 在调试过程中查看:
Xcode → Debug Navigator → 查看线程数量变化
监控指标:
- 实时线程数量
- 线程状态(运行/休眠)
- CPU 使用率
添加线程创建日志
// 在代码中添加线程创建跟踪
- (void)trackThreadCreation {
NSLog(@"线程创建 - 调用堆栈: %@", [NSThread callStackSymbols]);
NSLog(@"当前线程数: %lu", [self getThreadCount]);
}
- (NSUInteger)getThreadCount {
thread_act_array_t threads;
mach_msg_type_number_t count;
task_threads(mach_task_self(), &threads, &count);
vm_deallocate(mach_task_self(), (vm_address_t)threads, count * sizeof(thread_t));
return count;
}
3. 代码审查和静态分析
检查 GCD 使用模式
// 检查以下模式:
// 1. 循环中创建大量异步任务
for (int i = 0; i < 1000; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// 快速执行的任务
});
}
// 2. 未正确使用信号量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// 工作
// 忘记调用 dispatch_semaphore_signal
});
检查 NSThread 管理
// 检查线程是否正确退出
- (void)problematicThread {
@autoreleasepool {
// 工作代码
// 缺少 [NSThread exit] 或退出条件
}
}
4. 动态调试技巧
添加线程生命周期钩子
// 使用 Method Swizzling 跟踪线程创建
@implementation NSThread (ThreadLeakDetection)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalSelector = @selector(init);
SEL swizzledSelector = @selector(swizzled_init);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
method_exchangeImplementations(originalMethod, swizzledMethod);
});
}
- (instancetype)swizzled_init {
NSLog(@"线程创建: %@", [NSThread callStackSymbols]);
return [self swizzled_init];
}
@end
实时监控线程数量
// 定期检查线程数量
- (void)startThreadMonitoring {
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(checkThreadCount) userInfo:nil repeats:YES];
}
- (void)checkThreadCount {
mach_msg_type_number_t count;
thread_act_array_t threads;
kern_return_t kr = task_threads(mach_task_self(), &threads, &count);
if (kr == KERN_SUCCESS) {
NSLog(@"当前线程数量: %d", count);
vm_deallocate(mach_task_self(), (vm_address_t)threads, count * sizeof(thread_t));
// 设置阈值报警
if (count > 50) {
NSLog(@"⚠️ 线程数量异常: %d", count);
}
}
}
5. 使用第三方工具
PLCrashReporter
// 集成崩溃报告,分析线程状态
#import <PLCrashReporter/PLCrashReporter.h>
- (void)analyzeThreads {
PLCrashReporter *reporter = [[PLCrashReporter alloc] initWithConfiguration:[PLCrashReporterConfig defaultConfiguration]];
NSData *crashData = [reporter generateLiveReport];
// 分析线程信息
}
FBRetainCycleDetector
// 检查循环引用导致的线程无法释放
#import <FBRetainCycleDetector/FBRetainCycleDetector.h>
- (void)checkRetainCycles {
FBRetainCycleDetector *detector = [FBRetainCycleDetector new];
[detector addCandidate:self];
NSSet *cycles = [detector findRetainCycles];
NSLog(@"发现循环引用: %@", cycles);
}
6. 最佳实践检查清单
代码审查要点:
- GCD 队列使用是否正确
- NSOperationQueue 并发数设置是否合理
- NSThread 是否正确退出
- RunLoop 是否在适当时候停止
- 信号量使用是否成对
- 线程池大小是否合理
性能测试场景:
- 长时间运行测试
- 高并发压力测试
- 内存压力测试
- 后台/前台切换测试
通过以上方法的组合使用,可以系统地定位和解决线程泄露问题。建议从 Instruments 工具开始,结合代码审查和动态监控,逐步缩小问题范围。