You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 10:00:47