调用removeItemAtPath时发生崩溃问题求助
分析与解决方案
从你提供的崩溃日志来看,问题出现在-[XYPHPostModel(Manager) removeDraft:]调用NSFileManager removeItemAtPath:error:时触发了EXC_BAD_ACCESS KERN_PROTECTION_FAILURE——这通常意味着内存访问违规或者文件操作的竞态冲突,结合你描述的“删除后解归档失败、无法复现但用户普遍遇到”的情况,我整理了核心排查方向和修复方案:
1. 优先解决:文件操作的竞态条件
虽然你在主线程调用save和remove,但如果save(archive)是异步执行文件写入(比如后台线程完成归档写入),主线程的remove可能会和正在进行的写入操作冲突——NSFileManager在文件被占用时的底层操作可能触发内存异常。
修复方案:串行化所有文件操作
创建一个全局串行队列,将归档、解归档、删除等所有文件操作都放到这个队列里执行,确保同一时间只有一个操作访问目标文件:
// 在XYPHPostModel(Manager)中定义全局串行队列 static dispatch_queue_t kDraftFileQueue; + (void)load { kDraftFileQueue = dispatch_queue_create("com.yourapp.draft.file.queue", DISPATCH_QUEUE_SERIAL); } // 修改save方法,放到串行队列执行 - (void)saveArchive { dispatch_async(kDraftFileQueue, ^{ NSString *draftPath = self.draftFilePath; // 确保路径是可靠的强引用 NSData *archiveData = [NSKeyedArchiver archivedDataWithRootObject:self requiringSecureCoding:NO error:nil]; // 使用原子写入避免文件损坏 BOOL saveSuccess = [archiveData writeToFile:draftPath atomically:YES]; if (!saveSuccess) { NSLog(@"Failed to save draft to path: %@", draftPath); } }); } // 修改removeDraft方法,放到串行队列执行 - (void)removeDraft:(NSString *)draftPath { dispatch_async(kDraftFileQueue, ^{ // 先确保路径是有效且强引用的 NSString *safePath = [draftPath copy]; if (!safePath.length) return; NSFileManager *fm = [NSFileManager defaultManager]; NSError *error = nil; if ([fm fileExistsAtPath:safePath]) { BOOL removeSuccess = [fm removeItemAtPath:safePath error:&error]; if (!removeSuccess) { NSLog(@"Failed to remove draft: %@, error: %@", safePath, error); } } }); }
2. 排查内存野指针问题
KERN_PROTECTION_FAILURE也可能是因为传入removeItemAtPath:的draftPath是已释放的野指针,或者调用removeDraft:时XYPHPostModel实例已经被销毁。
修复方案:
- 确保
draftPath在使用时是强引用的:不要直接传递临时生成的字符串(比如未被持有的局部变量),在removeDraft:里先复制一份路径再操作(如上面代码中的safePath)。 - 在触发
removeDraft:的回调中使用weak-strong dance,避免访问已释放的对象:
// 比如在API成功回调中调用finishPost时 __weak typeof(self) weakSelf = self; [self.apiClient postToServer:params success:^(id response) { __strong typeof(weakSelf) strongSelf = weakSelf; if (!strongSelf) return; // 确保对象未被释放 [strongSelf setPostStatusToSuccess:YES]; [strongSelf finishPost]; // 该方法内部会调用removeDraft } failure:^(NSError *error) { // 处理失败逻辑 }];
3. 辅助排查:添加日志与错误捕获
由于你无法复现,建议在关键位置添加日志,收集用户端的崩溃上下文:
- (void)removeDraft:(NSString *)draftPath { // 记录路径和当前对象状态 NSLog(@"Removing draft: path=%@, model=%@, model retain count=%ld", draftPath, self, (long)CFGetRetainCount((__bridge CFTypeRef)self)); // 执行删除操作 // ... }
同时,对removeItemAtPath:的错误进行捕获并上报,帮助定位是否存在文件权限、文件不存在等系统级问题。
4. 归档可靠性优化
删除后解归档失败,可能是归档文件本身损坏导致的,建议:
- 始终使用
writeToFile:atomically:YES进行归档写入,原子写入会先创建临时文件,写入完成后再替换原文件,避免写入过程中被中断导致文件损坏。 - 解归档时增加错误判断:
+ (instancetype)loadDraftFromPath:(NSString *)path { NSError *error = nil; NSData *data = [NSData dataWithContentsOfFile:path options:0 error:&error]; if (!data) { NSLog(@"Failed to load draft data: %@", error); return nil; } id model = [NSKeyedUnarchiver unarchivedObjectOfClass:[self class] fromData:data error:&error]; if (error) { NSLog(@"Failed to unarchive draft: %@", error); // 如果解归档失败,主动删除损坏的文件 [[NSFileManager defaultManager] removeItemAtPath:path error:nil]; return nil; } return model; }
内容的提问来源于stack exchange,提问作者waving color
相关产品推荐
相关产品推荐

