我有一个适用于Mac和iOS的应用程序,它使用.plist文件在设备之间使用iCloud同步数据。每隔一段时间我就会收到一封来自特定设备上反复崩溃的人的电子邮件。 (其他设备都很好。)在每种情况下,应用程序在尝试从iCloud读取文件时崩溃,使用NSPropertyListSerialization将数据转换为字典。在每种情况下,通过让用户删除应用程序的iCloud数据来解决问题。相同的文件重新同步,一切正常。
我将在这里使用的具体示例来自Mac版本,但我在iOS上遇到了几乎相同的崩溃。从崩溃报告中:
Exception Type: EXC_BAD_ACCESS (SIGBUS)
Exception Codes: 0x000000000000000a, 0x0000000101ee2000
VM Regions Near 0x101ee2000:
VM_ALLOCATE 0000000101ee1000-0000000101ee2000 [ 4K] rw-/rwx SM=PRV
--> mapped file 0000000101ee2000-0000000101ee3000 [ 4K] r--/rwx SM=COW /Users/USER/Library/Mobile Documents/PB4R74AA4J~com~junecloud~Notefile/*/*.notefile
shared memory 0000000101ee3000-0000000101ee4000 [ 4K] rw-/rw- SM=SHM
然后:
Thread 7 Crashed:
0 libsystem_c.dylib 0x0000000105157013 bcmp + 19
1 com.apple.CoreFoundation 0x0000000101bbf4b0 __CFBinaryPlistGetTopLevelInfo + 80
2 com.apple.CoreFoundation 0x0000000101bbf36d __CFTryParseBinaryPlist + 93
3 com.apple.CoreFoundation 0x0000000101bbede2 _CFPropertyListCreateWithData + 146
4 com.apple.CoreFoundation 0x0000000101bcbdb0 CFPropertyListCreateWithData + 112
5 com.apple.Foundation 0x0000000101568b89 +[NSPropertyListSerialization propertyListWithData:options:format:error:] + 94
6 com.junecloud.Notefile-Helper 0x000000010107ad06 -[JUNNoteDocument readFromData:ofType:error:] + 64
7 com.apple.AppKit 0x000000010268d507 -[NSDocument readFromURL:ofType:error:] + 546
8 com.apple.AppKit 0x000000010228e3c8 -[NSDocument _initWithContentsOfURL:ofType:error:] + 135
9 com.apple.AppKit 0x000000010228e074 -[NSDocument initWithContentsOfURL:ofType:error:] + 262
10 com.junecloud.Notefile-Helper 0x000000010107cad8 -[JUNSyncDocument initWithFileURL:] + 213
11 com.junecloud.Notefile-Helper 0x0000000101079ec7 -[NotefileAppDelegate documentForURL:] + 68
12 com.junecloud.Notefile-Helper 0x00000001010825cb -[JUNSyncManager documentForURL:] + 76
13 com.junecloud.Notefile-Helper 0x000000010107d43c -[JUNSyncInOperation main] + 1340
14 com.apple.Foundation 0x0000000101563cd2 __NSThread__main__ + 1345
15 libsystem_c.dylib 0x00000001051697a2 _pthread_start + 327
16 libsystem_c.dylib 0x00000001051561e1 thread_start + 13
Thread 7 crashed with X86 Thread State (64-bit):
rax: 0x0000000000000062 rbx: 0x000000000000007b rcx: 0x000000010c2be868 rdx: 0x0000000000000007
rdi: 0x0000000101d1e151 rsi: 0x0000000101ee2000 rbp: 0x000000010c2be810 rsp: 0x000000010c2be7b8
r8: 0x000000010c2be870 r9: 0x0000000000000000 r10: 0x00007ff841c28900 r11: 0x00007ff841c186d8
r12: 0x000000010c2be870 r13: 0x0000000101ee2000 r14: 0x000000010c2beb00 r15: 0x000000010c2be980
rip: 0x0000000105157013 rfl: 0x0000000000010202 cr2: 0x0000000101ee2000
Logical CPU: 2
以下是崩溃的相关代码:
- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError {
BOOL success = NO;
NSDictionary *dictionary = nil;
NSError *error = nil;
id plist = [NSPropertyListSerialization propertyListWithData:data
options:NSPropertyListImmutable format:NULL error:&error];
if (plist && [plist isKindOfClass:[NSDictionary class]]) {
dictionary = plist;
} else {
NSLog(@"Error opening document: %@ %@",error,[error userInfo]);
if (outError != NULL) *outError = error;
}
// Then it does some things with the dictionary if it's not nil
return success;
}
我认为NSPropertyListSerialization只是对某些损坏的数据感到窒息,还是我自己的代码中更容易出现这个问题?如果问题出在NSPropertyListSerialization上,我可以采取哪些措施来防止应用程序崩溃,并更恰当地处理问题?
如果问题可能出现在我自己的代码中,我该怎么做才能找到原因?如果我可以自己复制问题,这会容易得多,但我从未在自己的设备上看到这种崩溃,显然我不能指望用户允许我访问他们的iCloud帐户。
更新根据要求,这里有点JUNSyncDocument
。在iOS上,这是一个UIDocument子类,在Mac上它是一个NSDocument子类。
- (id)initWithFileURL:(NSURL *)url {
#if TARGET_OS_IPHONE
self = [super initWithFileURL:url];
return self;
#else
NSError *error = nil;
if ([[NSFileManager defaultManager] fileExistsAtPath:[url path]]) {
if ((self = [super initWithContentsOfURL:url ofType:[self documentType] error:&error])) {
self.fileURL = url;
self.hasUndoManager = NO;
} else NSLog(@"Error initializing existing document: %@ %@",url,error);
} else {
if ((self = [super initWithType:[self documentType] error:&error])) {
self.fileURL = url;
self.hasUndoManager = NO;
} else NSLog(@"Error initializing new document: %@",error);
}
return self;
#endif
}
这看起来很混乱,如果我在这里做些蠢事,我也不会感到惊讶。但在大多数情况下它工作正常。这是从NotefileAppDelegate
调用的:
- (JUNSyncDocument *)documentForURL:(NSURL *)url {
JUNNoteDocument *document = [[JUNNoteDocument alloc] initWithFileURL:url];
return document;
}
反过来由JUNSyncManager
调用:
- (JUNSyncDocument *)documentForURL:(NSURL *)url {
return [self.delegate documentForURL:url];
}
由JUNSyncInOperation
调用:
JUNSyncDocument *document = [self.delegate documentForURL:self.url];
我刚刚意识到我可以使用NSClassFromString
摆脱那个荒谬的委托链,但我不知道这是否会影响问题。
答案 0 :(得分:3)
查看开源的the source of __CFTryParseBinaryPlist
and __CFBinaryPlistGetTopLevelInfo
。
看起来崩溃的memcmp(bcmp)就在它开始检查二进制plist头的数据的前几个字节的最开始。如果CFDataGetLength
为<= 8
字节,那么它不会那么远,所以它不是缓冲区下溢。可能CFDataGetBytePtr
返回nil,但我不知道如果长度为&gt; 8.最有可能的是,数据指针无效。
我可以告诉你更多你从崩溃报告中发布了注册内容。另外,发布如何创建数据的代码(-[JUNSyncDocument initWithFileURL:]
和-[NotefileAppDelegate documentForURL:]
。)
答案 1 :(得分:1)
我遇到完全相同的问题,(UIDocument
&amp; NSDocument
子类,访问plist数据时崩溃了),尽管我使用的是NSFileWrapper
。
映射文件路径在您的报告中看起来可疑(除非它已被操作系统匿名),看起来不是实际文件,而是查询字符串。是不是NSMetadataQuery
返回的结果不正确?
到目前为止我还没有解决方案,仍在搜索。
我可以向客户调试此问题,生成带有日志记录信息的自定义版本。以下是我的观察:
NSPropertyListSerialization
,CGImageSourceCreateWithURL
,还有许多其他API。它具有以下状态:
fileExists = YES;
isUbiquitous = YES;
hasNonZeroSize = YES;
isDownloaded = NO;
因此它作为大多数API的常规文件出现,但它不可用。访问时,它会导致应用程序崩溃。
- 访问的文件可以在具有isDownloaded = YES
的文件包中,尽管访问的文件本身(在文件包中)具有isDownloaded = NO
。
解决方法:在访问其内容之前,检查文件的isDownloaded
属性。使用以下方法检查此属性:
-[NSURL getResourceValue:&val forKey:NSURLUbiquitousItemIsDownloadedKey error:&error]
如果像我一样你使用NSFileWrapper
来阅读UIDocument
内容,那么你需要办理入住手续:
-[UIDocument readFromURL:(NSURL *)url error:(NSError *__autoreleasing *)outError]
因为NSFileWrapper
不允许您访问所需的文件包NSURL
。
我怀疑创建文档时iCloud的原子性有问题。看起来文件包及其内容与其下载状态不同步,就像首先创建文档目录一样,并将其内容作为2个不同的操作复制到它。