cocoa-touch - 如何防止 NSPropertyListSerialization 崩溃?

标签 cocoa-touch cocoa

我有一个适用于 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 摆脱那个荒谬的委托(delegate)链,但我不知道这是否会影响问题。

最佳答案

查看 the source of __CFTryParseBinaryPlist and __CFBinaryPlistGetTopLevelInfo ,它们是开源的。

看起来崩溃的 memcmp (bcmp) 是在一开始检查二进制 plist header 数据的前几个字节。如果 CFDataGetLength 就不会那么远是<= 8字节,所以它不是缓冲区下溢。有可能CFDataGetBytePtr返回 nil,但我不知道如果长度> 8,会发生这种情况。最有可能的是,数据指针已无效。

我可以告诉您更多信息,您发布了崩溃报告中的寄存器内容。另外,发布如何创建数据的代码( -[JUNSyncDocument initWithFileURL:]-[NotefileAppDelegate documentForURL:] 。)

关于cocoa-touch - 如何防止 NSPropertyListSerialization 崩溃?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15980277/

相关文章:

iphone - 关联对象何时被释放?

objective-c - 在图像上应用多个 CIFilter

cocoa - 如何执行操作来响应添加到 NSArrayController 内容的项目?

ios - UITextView 的表格单元格高度,其中内容对齐方式为 NSTextAlignmentJustified

objective-c - 如何在 tableView 单元格中显示多行

objective-c - 检测 UIView 是否与其他 UIView 相交

iphone - UItable 标题在滚动时始终显示

swift - 水平和垂直嵌套 ScrollView

ios - 我如何从 viewController 中提取死元素

cocoa - 在 NSRect 中绘制图像