当我将文件包从 Finder 拖到 iTunes.app 文件共享窗格中时,我的 UIDocument 类正在尝试读取关联的文件包装器,并且看起来我读取它的代码执行速度比 iTunes.app 复制内容的速度快. 初始文件包装器数组仅包含包装器内的两个文件之一。
那么我如何“减慢”我的代码呢?
我使用 performSelector:withObject:AfterDelay:1.0f 进行了测试,效果很好,但感觉真的很冒险:如果将一个非常大的文件拖入 iTunes 怎么办(我的意思是一个非常大的文件超过了我的延迟)?如果同时放置多个文件怎么办?
我看着以某种方式辨别文件已准备好读取,但苹果的 NSFileManager 文档说
“尝试一项操作(例如加载文件或创建目录)、检查错误并优雅地处理这些错误要比尝试提前确定操作是否会成功要好得多。”
但是出现时间问题的地方是在解码组成文件包装器的过程中,所以如何“优雅地”处理它让我望而却步。
我的包文件中有两个数据文件(在这个早期阶段,但设计是因为会有更多):data.dat 和 info.dat。当我第一次让代码工作到可以将文件拖到 iTunes.app 并在我的视图控制器中注意到它的地步时,data.dat 总是可以正常解码,但找不到 info.dat。一旦文件位于我的本地文档文件夹中,视图控制器就会按预期显示所有数据(即 info.dat 位于文件包装器内并且格式正确)。怀疑这是一个时间问题,我将文件名常量和磁盘文件重命名为 zdata.dat 和 ainfo.dat —— 果然:加载了 ainfo.dat 并且我的 UIDocument 子类抱怨找不到 zdata.dat。
我使用延迟加载,但视图控制器对 info.dat 内容有直接的兴趣,因此延迟加载不足以让 iTunes 完成复制!
从我的 UIDocument 子类实现:
- (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError *__autoreleasing *)outError
{
self.fileWrapper = (NSFileWrapper *)contents;
// Lazy load everything!
_data = nil;
_metadata = nil;
return YES;
}
- (id)decodeObjectFromWrapperWithPreferredFilename:(NSString *)preferredFilename
{
NSFileWrapper *fw = [self.fileWrapper.fileWrappers objectForKey:preferredFilename];
if (!fw) {
NSLog(@"Unexpected error: Couldn't find %@ in the file wrapper for %@", preferredFilename, self.fileURL);
return nil;
}
NSData *data = [fw regularFileContents];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
return [unarchiver decodeObjectForKey:@"data"];
}
- (GSBMetadata *)metadata
{
if (_metadata == nil) {
if (self.fileWrapper != nil) {
// NSLog(@"Loading metadata for %@...",self.fileURL);
_metadata = [self decodeObjectFromWrapperWithPreferredFilename:kGSBMetadataFileName];
} else {
_metadata = [[GSBMetadata alloc] init];
}
}
return _metadata;
}
问题是在 decodeObjectFromWrapperWithPreferredFilename: 中发现的,当进行检查以确保存在预期的文件包装器时。通常情况下,如果文件损坏,或者应用程序的版本 2 使用了不同的文件格式,就会出现这种情况。但是优雅地处理这些情况属于“偏执的编程预期那种事情”的标题,而不是“稍等片刻,您的所有数据都将提供给您”的标题。