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

iOS:为NSData的writeToFile添加完成回调以显示PDF文件

嘿,这个需求其实很明确——就是要保证文件100%写入完成后再启动预览,避免出现文件还没写完就去读取导致的预览失败或者崩溃问题。下面给你两种实用的实现方式,你可以根据自己的iOS版本选择:

方案1:用GCD实现异步写入+完成回调

直接在主线程调用writeToFile:atomically:会阻塞UI,所以我们把写入操作放到后台队列执行,完成后再切回主线程处理预览逻辑(UI操作必须在主线程):

// 假设你已经拿到了PDF的NSData对象pdfData
NSString *filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"temp.pdf"];

// 后台队列执行写入操作
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    BOOL writeSuccess = [pdfData writeToFile:filePath atomically:YES];
    
    // 切回主线程处理UI逻辑
    dispatch_async(dispatch_get_main_queue(), ^{
        if (writeSuccess) {
            // 文件写入成功,启动QLPreviewController
            QLPreviewController *previewVC = [[QLPreviewController alloc] init];
            previewVC.dataSource = self;
            previewVC.currentPreviewItemIndex = 0;
            [self presentViewController:previewVC animated:YES completion:nil];
        } else {
            // 写入失败,给用户提示
            UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提示" message:@"PDF文件保存失败" preferredStyle:UIAlertControllerStyleAlert];
            [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil]];
            [self presentViewController:alert animated:YES completion:nil];
        }
    });
});

// 别忘了实现QLPreviewControllerDataSource协议方法
- (NSInteger)numberOfPreviewItemsInPreviewController:(QLPreviewController *)controller {
    return 1;
}

- (id<QLPreviewItem>)previewController:(QLPreviewController *)controller previewItemAtIndex:(NSInteger)index {
    NSString *filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"temp.pdf"];
    return [NSURL fileURLWithPath:filePath];
}
方案2:用FileManager的现代异步写入API(iOS 13+)

苹果在iOS13之后提供了更简洁的异步写入API,自带完成回调,不用手动管理GCD队列,代码更清爽:

// 准备好pdfData和文件路径
NSString *filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"temp.pdf"];
NSURL *fileURL = [NSURL fileURLWithPath:filePath];

// 异步写入,完成后触发回调
[pdfData writeToURL:fileURL options:NSDataWritingAtomic completionHandler:^(NSError * _Nullable error) {
    // 切回主线程处理UI
    dispatch_async(dispatch_get_main_queue(), ^{
        if (!error) {
            // 写入成功,启动预览
            QLPreviewController *previewVC = [[QLPreviewController alloc] init];
            previewVC.dataSource = self;
            previewVC.currentPreviewItemIndex = 0;
            [self presentViewController:previewVC animated:YES completion:nil];
        } else {
            // 处理写入错误
            UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"错误" message:[error localizedDescription] preferredStyle:UIAlertControllerStyleAlert];
            [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil]];
            [self presentViewController:alert animated:YES completion:nil];
        }
    });
}];

// 同样需要实现QLPreviewControllerDataSource协议方法(同方案1)

几个关键注意点:

  • 所有UI操作(比如弹出预览控制器、提示框)必须放在主线程执行,否则可能出现UI异常或者崩溃。
  • 开启NSDataWritingAtomic(也就是atomically:YES)会先把文件写入临时文件,完成后再替换目标文件,能避免写入中断导致的文件损坏,建议始终开启。
  • 如果需要重复写入同文件名的PDF,建议每次写入前检查文件是否存在,必要时删除旧文件,避免冲突。

内容的提问来源于stack exchange,提问作者user979331

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 09:58:57