【问题标题】:configurePersistentStoreCoordinator not called for saveAs NSPersistentDocument未为 saveAs NSPersistentDocument 调用 configurePersistentStoreCoordinator
【发布时间】:2018-07-19 19:41:48
【问题描述】:

我在保存 NSPersistentDocument 时遇到了奇怪的行为。我可以创建一个自动保存的新文档而不会出现问题。但是当我保存它时 write(to: ofType: for: absoluteOriginalContentsURL:) 被调用但它转向 configurePersistentStoreCoordinator(for: ofType: modelConfiguration: storeOptions:) 未被调用。这很不幸,因为我需要配置商店。原因是我需要将 NSColor 注册为可解码的options[NSBinaryStoreSecureDecodingClasses] = NSSet(object: NSColor.self)

第一次尝试是自己调用它,但这并没有引导我到任何地方。自己调用它并没有按预期注册设置。

我的 NSPersistentDocument 中的代码如下所示:

 override func configurePersistentStoreCoordinator(for url: URL, ofType fileType: String, modelConfiguration configuration: String?, storeOptions: [String : Any]? = nil) throws {
        Swift.print("VTDocment.configurePersistentStoreCoordinator for \(url.lastPathComponent)")
        var options = addOptions(to: storeOptions)
        try super.configurePersistentStoreCoordinator(for: url, ofType: fileType, modelConfiguration: configuration, storeOptions: options)
    }      

    func addOptions(to: [String : Any]?) -> [String : Any] {
        var options = to != nil ? to! : [String:Any]()
        if #available(OSX 10.13, *) {
            options[NSBinaryStoreSecureDecodingClasses] = NSSet(object: NSColor.self)
        }
        options[NSMigratePersistentStoresAutomaticallyOption] = true
        options[NSInferMappingModelAutomaticallyOption] = true
        return options
    } 

override func write(to url: URL, ofType typeName: String, for saveOperation: NSDocument.SaveOperationType, originalContentsURL absoluteOriginalContentsURL: URL?) throws {
    try self.configurePersistentStoreCoordinator(for: url, ofType: typeName, modelConfiguration: "Document")
    do {
        Swift.print("Now... VTDocment.write to \(url.lastPathComponent)")
        try super.write(to: url, ofType: typeName, for: saveOperation, originalContentsURL: absoluteOriginalContentsURL)
    } catch {
        Swift.print("VTDocment.write error: \(error)")
    }
}

结果是以下输出(第二个 configurePersistentStoreCoordinator 输出在通过菜单命令另存为之后开始):

VTDocment.configurePersistentStoreCoordinator for Unsaved Visual Thinking with IBIS Document 12.ibisVT
VTDocment.configurePersistentStoreCoordinator for test.ibisVT
Now... VTDocment.write to test.ibisVT
value for key 'NS.objects' was of unexpected class 'NSColor'. Allowed classes are '{(
    NSNumber,
    NSString,
    NSCalendarDate,
    NSOrderedSet,
    NSDecimalNumber,
    NSUUID,
    NSDate,
    NSSet,
    NSNull,
    NSURL,
    NSData,
    NSDictionaryMapNode,
    NSDictionary,
    NSArray
)}'.
(null)

105827995370488

2018-02-09 05:53:52.250312+0100 Visual Thinking with IBIS[42589:19295813] -[NSException initialize]: unrecognized selector sent to instance 0x60400025d7c0
value for key 'NS.objects' was of unexpected class 'NSColor'. Allowed classes are '{(
    NSNumber,
    NSString,
    NSCalendarDate,
    NSOrderedSet,
    NSDecimalNumber,
    NSUUID,
    NSDate,
    NSSet,
    NSNull,
    NSURL,
    NSData,
    NSDictionaryMapNode,
    NSDictionary,
    NSArray
)}'.

编辑 感谢@Tora 的提问!这是一个相关的堆栈跟踪。它不是来自同一个会话,而是导致相同的结果。

    0   CoreFoundation                      0x00007fff40f9ffcb __exceptionPreprocess + 171
1   libobjc.A.dylib                     0x00007fff67c41c76 objc_exception_throw + 48
2   Foundation                          0x00007fff430ef08f -[NSCoder(Exceptions) __failWithExceptionName:errorCode:format:] + 0
3   Foundation                          0x00007fff430ef20c -[NSCoder(Exceptions) __failWithExceptionName:errorCode:format:] + 381
4   Foundation                          0x00007fff43017555 -[NSCoder _validateAllowedClass:forKey:allowingInvocations:] + 239
5   Foundation                          0x00007fff42fd948c _decodeObjectBinary + 1944
6   Foundation                          0x00007fff42fdaa6f -[NSKeyedUnarchiver _decodeArrayOfObjectsForKey:] + 1945
7   Foundation                          0x00007fff42fde8c5 -[NSArray(NSArray) initWithCoder:] + 202
8   Foundation                          0x00007fff42fd9523 _decodeObjectBinary + 2095
9   Foundation                          0x00007fff42fd8bee _decodeObject + 417
10  Foundation                          0x00007fff42ff171b -[NSKeyedUnarchiver decodeObjectOfClasses:forKey:] + 409
11  CoreData                            0x00007fff40b77491 -[NSDictionaryMapNode initWithCoder:] + 97
12  Foundation                          0x00007fff42fd9523 _decodeObjectBinary + 2095
13  Foundation                          0x00007fff42fdaa6f -[NSKeyedUnarchiver _decodeArrayOfObjectsForKey:] + 1945
14  Foundation                          0x00007fff42fd9de8 -[NSDictionary(NSDictionary) initWithCoder:] + 202
15  Foundation                          0x00007fff42fd9523 _decodeObjectBinary + 2095
16  Foundation                          0x00007fff42fd8bee _decodeObject + 417
17  Foundation                          0x00007fff42ff171b -[NSKeyedUnarchiver decodeObjectOfClasses:forKey:] + 409
18  CoreData                            0x00007fff40bcfcc2 -[NSBinaryObjectStoreFile readBinaryStoreFromData:originalPath:error:] + 994
19  CoreData                            0x00007fff40bd019a -[NSBinaryObjectStoreFile readFromFile:error:] + 474
20  CoreData                            0x00007fff40aed247 -[NSDictionaryStoreMap initWithStore:fromPath:] + 215
21  CoreData                            0x00007fff40b75bd3 -[NSBinaryObjectStore initWithPersistentStoreCoordinator:configurationName:URL:options:] + 739
22  CoreData                            0x00007fff40b56e35 __91-[NSPersistentStoreCoordinator addPersistentStoreWithType:configuration:URL:options:error:]_block_invoke + 1845
23  CoreData                            0x00007fff40b69132 gutsOfBlockToNSPersistentStoreCoordinatorPerform + 210
24  CoreData                            0x00007fff40b52f75 _perform + 213
25  CoreData                            0x00007fff40a7d2cf -[NSPersistentStoreCoordinator addPersistentStoreWithType:configuration:URL:options:error:] + 559
26  CoreData                            0x00007fff40b596e4 __84-[NSPersistentStoreCoordinator migratePersistentStore:toURL:options:withType:error:]_block_invoke + 1284
27  CoreData                            0x00007fff40b69132 gutsOfBlockToNSPersistentStoreCoordinatorPerform + 210
28  libdispatch.dylib                   0x00007fff687f7d50 _dispatch_client_callout + 8
29  libdispatch.dylib                   0x00007fff6880b1d6 _dispatch_queue_barrier_sync_invoke_and_complete + 60
30  CoreData                            0x00007fff40b52f62 _perform + 194
31  CoreData                            0x00007fff40b590ef -[NSPersistentStoreCoordinator migratePersistentStore:toURL:options:withType:error:] + 351
32  AppKit                              0x00007fff3ebc8fd6 -[NSPersistentDocument writeToURL:ofType:forSaveOperation:originalContentsURL:error:] + 2367
33  Visual Thinking with IBIS           0x000000010003edb2 _T025Visual_Thinking_with_IBIS10VTDocumentC5writey10Foundation3URLV2to_SS6ofTypeSo10NSDocumentC013SaveOperationK0O3forAGSg016originalContentsH0tKF + 1570
34  Visual Thinking with IBIS           0x000000010003f22c _T025Visual_Thinking_with_IBIS10VTDocumentC5writey10Foundation3URLV2to_SS6ofTypeSo10NSDocumentC013SaveOperationK0O3forAGSg016originalContentsH0tKFTo + 284
35  AppKit                              0x00007fff3f0996aa -[NSDocument(NSDocumentSaving) _writeSafelyToURL:ofType:forSaveOperation:forceTemporaryDirectory:error:] + 870
36  AppKit                              0x00007fff3f09a394 -[NSDocument(NSDocumentSaving) _writeSafelyToURL:ofType:forSaveOperation:error:] + 28
37  AppKit                              0x00007fff3ebc9eb3 -[NSPersistentDocument writeSafelyToURL:ofType:forSaveOperation:error:] + 446
38  AppKit                              0x00007fff3f0a5294 __85-[NSDocument(NSDocumentSaving) _saveToURL:ofType:forSaveOperation:completionHandler:]_block_invoke_2.1146 + 238
39  AppKit                              0x00007fff3f0a5197 __85-[NSDocument(NSDocumentSaving) _saveToURL:ofType:forSaveOperation:completionHandler:]_block_invoke.1143 + 454
40  AppKit                              0x00007fff3f0a33ff __85-[NSDocument(NSDocumentSaving) _saveToURL:ofType:forSaveOperation:completionHandler:]_block_invoke_3.999 + 1962
41  AppKit                              0x00007fff3eae0eb4 -[NSDocument(NSDocumentSerializationAPIs) continueFileAccessUsingBlock:] + 238
42  AppKit                              0x00007fff3f0a04b9 __119-[NSDocument(NSDocumentSaving) _fileCoordinator:asynchronouslyCoordinateReadingContentsAndWritingItemAtURL:byAccessor:]_block_invoke_2 + 94
43  AppKit                              0x00007fff3ea5f8c0 __62-[NSDocumentController(NSInternal) _onMainThreadInvokeWorker:]_block_invoke.2153 + 157
44  CoreFoundation                      0x00007fff40f3858c __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 12
45  CoreFoundation                      0x00007fff40f1b043 __CFRunLoopDoBlocks + 275
46  CoreFoundation                      0x00007fff40f1ae08 __CFRunLoopRun + 3128
47  CoreFoundation                      0x00007fff40f19f43 CFRunLoopRunSpecific + 483
48  HIToolbox                           0x00007fff40231e26 RunCurrentEventLoopInMode + 286
49  HIToolbox                           0x00007fff40231b96 ReceiveNextEventCommon + 613
50  HIToolbox                           0x00007fff40231914 _BlockUntilNextEventMatchingListInModeWithFilter + 64
51  AppKit                              0x00007fff3e4fcf5f _DPSNextEvent + 2085
52  AppKit                              0x00007fff3ec92b4c -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 3044
53  AppKit                              0x00007fff3e4f1d6d -[NSApplication run] + 764
54  AppKit                              0x00007fff3e4c0f1a NSApplicationMain + 804
55  Visual Thinking with IBIS           0x0000000100019bed main + 13
56  libdyld.dylib                       0x00007fff68831115 start + 1

非常感谢。

更新 Tora 的解决方案(通过方法调配)工作正常。但是,有时(不确定何时)可能会导致相同的问题。这是更新的堆栈跟踪。这很神秘。 :-)

0   CoreFoundation                      0x00007fff40f9ffcb __exceptionPreprocess + 171
1   libobjc.A.dylib                     0x00007fff67c41c76 objc_exception_throw + 48
2   Foundation                          0x00007fff430ef08f -[NSCoder(Exceptions) __failWithExceptionName:errorCode:format:] + 0
3   Foundation                          0x00007fff430ef20c -[NSCoder(Exceptions) __failWithExceptionName:errorCode:format:] + 381
4   Foundation                          0x00007fff43017555 -[NSCoder _validateAllowedClass:forKey:allowingInvocations:] + 239
5   Foundation                          0x00007fff42fd948c _decodeObjectBinary + 1944
6   Foundation                          0x00007fff42fdaa6f -[NSKeyedUnarchiver _decodeArrayOfObjectsForKey:] + 1945
7   Foundation                          0x00007fff42fde8c5 -[NSArray(NSArray) initWithCoder:] + 202
8   Foundation                          0x00007fff42fd9523 _decodeObjectBinary + 2095
9   Foundation                          0x00007fff42fd8bee _decodeObject + 417
10  Foundation                          0x00007fff42ff171b -[NSKeyedUnarchiver decodeObjectOfClasses:forKey:] + 409
11  CoreData                            0x00007fff40b77491 -[NSDictionaryMapNode initWithCoder:] + 97
12  Foundation                          0x00007fff42fd9523 _decodeObjectBinary + 2095
13  Foundation                          0x00007fff42fdaa6f -[NSKeyedUnarchiver _decodeArrayOfObjectsForKey:] + 1945
14  Foundation                          0x00007fff42fd9de8 -[NSDictionary(NSDictionary) initWithCoder:] + 202
15  Foundation                          0x00007fff42fd9523 _decodeObjectBinary + 2095
16  Foundation                          0x00007fff42fd8bee _decodeObject + 417
17  Foundation                          0x00007fff42ff171b -[NSKeyedUnarchiver decodeObjectOfClasses:forKey:] + 409
18  CoreData                            0x00007fff40bcfcc2 -[NSBinaryObjectStoreFile readBinaryStoreFromData:originalPath:error:] + 994
19  CoreData                            0x00007fff40bd019a -[NSBinaryObjectStoreFile readFromFile:error:] + 474
20  CoreData                            0x00007fff40aed247 -[NSDictionaryStoreMap initWithStore:fromPath:] + 215
21  CoreData                            0x00007fff40b75bd3 -[NSBinaryObjectStore initWithPersistentStoreCoordinator:configurationName:URL:options:] + 739
22  CoreData                            0x00007fff40b56e35 __91-[NSPersistentStoreCoordinator addPersistentStoreWithType:configuration:URL:options:error:]_block_invoke + 1845
23  CoreData                            0x00007fff40b69132 gutsOfBlockToNSPersistentStoreCoordinatorPerform + 210
24  CoreData                            0x00007fff40b52f75 _perform + 213
25  CoreData                            0x00007fff40a7d2cf -[NSPersistentStoreCoordinator addPersistentStoreWithType:configuration:URL:options:error:] + 559
26  CoreData                            0x00007fff40b596e4 __84-[NSPersistentStoreCoordinator migratePersistentStore:toURL:options:withType:error:]_block_invoke + 1284
27  CoreData                            0x00007fff40b69132 gutsOfBlockToNSPersistentStoreCoordinatorPerform + 210
28  libdispatch.dylib                   0x00007fff687f7d50 _dispatch_client_callout + 8
29  libdispatch.dylib                   0x00007fff6880b1d6 _dispatch_queue_barrier_sync_invoke_and_complete + 60
30  CoreData                            0x00007fff40b52f62 _perform + 194
31  CoreData                            0x00007fff40b590ef -[NSPersistentStoreCoordinator migratePersistentStore:toURL:options:withType:error:] + 351
32  AppKit                              0x00007fff3ebc8fd6 -[NSPersistentDocument writeToURL:ofType:forSaveOperation:originalContentsURL:error:] + 2367
33  Visual Thinking with IBIS           0x00000001000404a2 _T025Visual_Thinking_with_IBIS10VTDocumentC5writey10Foundation3URLV2to_SS6ofTypeSo10NSDocumentC013SaveOperationK0O3forAGSg016originalContentsH0tKF + 1570
34  Visual Thinking with IBIS           0x000000010004091c _T025Visual_Thinking_with_IBIS10VTDocumentC5writey10Foundation3URLV2to_SS6ofTypeSo10NSDocumentC013SaveOperationK0O3forAGSg016originalContentsH0tKFTo + 284
35  AppKit                              0x00007fff3f0996aa -[NSDocument(NSDocumentSaving) _writeSafelyToURL:ofType:forSaveOperation:forceTemporaryDirectory:error:] + 870
36  AppKit                              0x00007fff3f09a394 -[NSDocument(NSDocumentSaving) _writeSafelyToURL:ofType:forSaveOperation:error:] + 28
37  AppKit                              0x00007fff3ebc9eb3 -[NSPersistentDocument writeSafelyToURL:ofType:forSaveOperation:error:] + 446
38  AppKit                              0x00007fff3f0a5294 __85-[NSDocument(NSDocumentSaving) _saveToURL:ofType:forSaveOperation:completionHandler:]_block_invoke_2.1146 + 238
39  AppKit                              0x00007fff3f0a5197 __85-[NSDocument(NSDocumentSaving) _saveToURL:ofType:forSaveOperation:completionHandler:]_block_invoke.1143 + 454
40  AppKit                              0x00007fff3f0a33ff __85-[NSDocument(NSDocumentSaving) _saveToURL:ofType:forSaveOperation:completionHandler:]_block_invoke_3.999 + 1962
41  AppKit                              0x00007fff3eae0eb4 -[NSDocument(NSDocumentSerializationAPIs) continueFileAccessUsingBlock:] + 238
42  AppKit                              0x00007fff3f0a04b9 __119-[NSDocument(NSDocumentSaving) _fileCoordinator:asynchronouslyCoordinateReadingContentsAndWritingItemAtURL:byAccessor:]_block_invoke_2 + 94
43  AppKit                              0x00007fff3ea5f8c0 __62-[NSDocumentController(NSInternal) _onMainThreadInvokeWorker:]_block_invoke.2153 + 157
44  CoreFoundation                      0x00007fff40f3858c __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 12
45  CoreFoundation                      0x00007fff40f1b043 __CFRunLoopDoBlocks + 275
46  CoreFoundation                      0x00007fff40f1ae08 __CFRunLoopRun + 3128
47  CoreFoundation                      0x00007fff40f19f43 CFRunLoopRunSpecific + 483
48  HIToolbox                           0x00007fff40231e26 RunCurrentEventLoopInMode + 286
49  HIToolbox                           0x00007fff40231b96 ReceiveNextEventCommon + 613
50  HIToolbox                           0x00007fff40231914 _BlockUntilNextEventMatchingListInModeWithFilter + 64
51  AppKit                              0x00007fff3e4fcf5f _DPSNextEvent + 2085
52  AppKit                              0x00007fff3ec92b4c -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 3044
53  AppKit                              0x00007fff3e4f1d6d -[NSApplication run] + 764
54  AppKit                              0x00007fff3e4c0f1a NSApplicationMain + 804
55  Visual Thinking with IBIS           0x000000010001a3dd main + 13
56  libdyld.dylib                       0x00007fff68831115 start + 1

【问题讨论】:

  • 我在 Apple 文档中发现了这一点:重要 NSPersistentDocument 不支持某些文档行为:[..] saveToOperation 操作类型。 Core Data 不支持将更改保存到新文档,同时在当前文档中保持未保存状态。来自:developer.apple.com/documentation/appkit/nspersistentdocument 这能解释这个吗?
  • 也许我需要自己迁移?为什么这不是框架的一部分?
  • Kneup 的向导,我刚刚试过发现,如你所说,通过菜单执行saveDocumentAs: 从开头实现的saveDocumentAs: 时不会调用configurePersistentStoreCoordinator。但在我的情况下没有发生错误。唔。然后,Open Recent保存的文件,调用协调器。请注意NSDocument 似乎隐藏了Save As... 菜单项并添加Duplicate。我手动将Save As For Test... 绑定添加到saveDocumentAs: 以进行复制。使用 Xcode 9.2 测试。
  • configurePersistentStoreCoordinator 的意图,据我了解,是为了保护应用程序免受恶意文件试图注入有害的类实例。因此,打开文件时应该调用它,而将文档保存到文件时则不需要调用它。我知道你已经知道了。关键可能是为什么在saveAs 时会发生错误。
  • 提示可能是-[NSException initialize]: unrecognized selector sent to instance 0x60400025d7c0,而不是value for key 'NS.objects' was of unexpected class 'NSColor'.... 我们能否看到异常发生时的堆栈跟踪。像这样stackoverflow.com/questions/28822289/… 当你在 Xcode 或 CrashReporter 上时,可以通过 (lldb) bt 命令获得 developer.apple.com/library/content/technotes/tn2004/…

标签: macos cocoa core-data


【解决方案1】:

另一种更简单的解决方法:

以下代码有一个错误。请参考下面引用的错误修复版本。

extension NSPersistentStoreCoordinator {

  @objc func x_migratePersistentStore(_ store: NSPersistentStore, to URL: URL, options: [AnyHashable : Any]? = nil, withType storeType: String) throws -> NSPersistentStore {
    var opt: [AnyHashable : Any] = options ?? [:]

    if #available(OSX 10.13, *) {
      opt[NSBinaryStoreSecureDecodingClasses] = NSSet(array: [ NSColor.self ])
    }

    return try x_migratePersistentStore(store, to: URL, options: opt, withType: storeType)
  }

}

class Document: NSPersistentDocument {

  override init() {
    super.init()

    let s1 = #selector(NSPersistentStoreCoordinator.migratePersistentStore(_:to:options:withType:))
    let s2 = #selector(NSPersistentStoreCoordinator.x_migratePersistentStore(_:to:options:withType:))
    let m1 = class_getInstanceMethod(NSPersistentStoreCoordinator.self, s1)!
    let m2 = class_getInstanceMethod(NSPersistentStoreCoordinator.self, s2)!
    method_exchangeImplementations(m1, m2)
  }

  override func configurePersistentStoreCoordinator(for url: URL, ofType fileType: String, modelConfiguration configuration: String?, storeOptions: [String : Any]? = nil) throws {
    // keep yours
  }

}

添加:

在他们增强NSPersistentDocument.write(to...) 以考虑选项或实施其他方法来处理选项之前,此解决方法将是一个实用的解决方案。这些选项将提供给NSPersistentStoreCoordinator.migratePersistentStore(...),然后用于实例化NSPersistentStore

错误修复版本:

前面的解决方法中有一个错误。打开文档文件、创建新文件、进行更改、等待文档自动保存三十秒、关闭它们和/或随机保存它们会导致初始错误。由 Kneup 向导报告。

这是一个使用单例的固定版本,以确保仅应用一次 swizzling。

extension NSPersistentStoreCoordinator {

  @objc func x_migratePersistentStore(_ store: NSPersistentStore, to URL: URL, options: [AnyHashable : Any]? = nil, withType storeType: String) throws -> NSPersistentStore {
    var opt: [AnyHashable : Any] = options ?? [:]

    if #available(OSX 10.13, *) {
      opt[NSBinaryStoreSecureDecodingClasses] = NSSet(array: [ NSColor.self ])
    }

    return try x_migratePersistentStore(store, to: URL, options: opt, withType: storeType)
  }

  class MigratePersistentStoreInitializer {
    init() {
      let s1 = #selector(NSPersistentStoreCoordinator.migratePersistentStore(_:to:options:withType:))
      let s2 = #selector(NSPersistentStoreCoordinator.x_migratePersistentStore(_:to:options:withType:))
      let m1 = class_getInstanceMethod(NSPersistentStoreCoordinator.self, s1)!
      let m2 = class_getInstanceMethod(NSPersistentStoreCoordinator.self, s2)!
      method_exchangeImplementations(m1, m2)
    }

    static let singlton = MigratePersistentStoreInitializer()  // Lazy Stored Property
  }

}

class Document: NSPersistentDocument {

  override init() {
    super.init()
    let _ = NSPersistentStoreCoordinator.MigratePersistentStoreInitializer.singlton
  }

  override func configurePersistentStoreCoordinator(for url: URL, ofType fileType: String, modelConfiguration configuration: String?, storeOptions: [String : Any]? = nil) throws {
    // keep yours
  }

}

参考资料:

【讨论】:

  • 我印象深刻!谢谢!
  • 这是我的荣幸!我还需要为我正在开发的应用程序提供解决方案。感谢您有机会考虑。
  • 有趣的是,错误再次弹出。我有 4 个文档一个接一个地打开和关闭。 (不确定 4 在这里是否重要)。当我关闭最后一个时,它会失败并显示相同的错误消息。 Stack Trace 显示了另外 2 个条目,我会将它们与您的方法调配相关联。我将堆栈跟踪添加到整体问题。你能确认一下吗?
  • 这很有趣!按照您的程序,我遇到了与您相同的错误并开始调查它。很抱歉这几天因为离线,所以回复晚了。
  • 我发现有一个错误。代码已更新。谢谢你的报告!!!
【解决方案2】:

感谢@Wizard of Kneup!

这清楚地表明应用程序确实读取了一个文档文件然后遇到了错误。

让我们开始调查吧。

(1) 应用尝试读取什么文件?

(lldb) breakpoint set -n '-[NSBinaryObjectStoreFile readFromFile:error:]'    
Breakpoint 3: # locations.

再次运行应用程序以重现问题。一旦断点命中,在 lldb 提示符下键入以下命令:

po $rdi
p (SEL)$rsi
po $rdx
po $rcx
po $r8 
po $r9

将显示文件名。禁用断点。使用之前设置的断点处返回的数字#。例如3

(lldb) breakpoint disable 3

(2) 应用尝试解码什么对象?

(lldb) breakpoint set -n '-[NSKeyedUnarchiver _decodeArrayOfObjectsForKey:]'
Breakpoint 4: # locations.

同上。使用一组po 命令来获取一些信息。这似乎是一个数组。

(lldb) breakpoint disable 4

(3) 应用拒绝解码什么类?

(lldb) breakpoint set -n '-[NSCoder _validateAllowedClass:forKey:allowingInvocations:]'

我一一写断点,每次都禁用。但是,没有必要这样做。或者,一次设置所有断点,然后点击并调查,然后继续。

问题是应用程序读取文件的原因。那可能是为了迁移。

-[NSPersistentStoreCoordinator migratePersistentStore:toURL:options:withType:error:]

但是为什么不调用override func configurePersistentStoreCoordinator()?我现在对此一无所知。

我希望你能得到一些提示,以便更接近解决方案。

添加:

要设置断点,有时我们需要在应用程序执行的最开始处停止。

例如,在AppDelegateinit() 处设置断点,以获得手动设置断点的机会。

class AppDelegate: NSObject, NSApplicationDelegate {

  override init() {
=>  super.init()
  }

}

附加:

我误会了。更正:

func configurePersistentStoreCoordinator 似乎在使用所需 URL(即文件名)实例化类 NSPersistentStore 时被调用。因此,时间与读取或写入文档文件的操作无关。相反,这是内部文档即将连接到 URL 的第一次。

  • 打开现有文件 -> 调用 func configurePersistentStoreCoordinator
  • 文件 > 新建然后保存... -> 调用函数。

如果文档是预先从现有文件中加载的,我们已经看到 func configurePersistentStoreCoordinator 未被调用 SaveAs 的事实似乎是正常的。我认为我们在该函数中提供的NSPersistentStore 选项已经存在。

设置断点的提示

【讨论】:

  • 附加调试提示:在断点导航器中设置、禁用和启用断点。您不必重新启动应用程序,只需打开和关闭一个文档。
  • 我不是这个意思。进入Breakpoint navigator,点击底部的+,选择Symbolic Breakpoint…,粘贴方法名,不带引号。
【解决方案3】:

好消息和坏消息

我已成功重现在 MacOS 10.13.2 上运行的 Xcode 9.2 完全相同的错误。

  1. 文档文件的版本 1 仍在自动保存目录中。
  2. Document.xcdatamodel 已更新到版本 2(Document 2.xcdatamodel)
  3. 应用程序启动时,使用NSBinaryStoreSecureDecodingClasses选项读取Autosave目录中版本1的文件。
  4. 该应用运行良好。
  5. 文件>保存...触发NSPersistentStoreCoordinator .migratePersistentStore()又名-[NSPersistentStoreCoordinator migratePersistentStore:toURL:options:withType:error:]:
  6. migratePersistentStore 的过程中,NSBinaryStoreSecureDecodingClasses 选项未设置用于写入和读取临时文件。
  7. 出现错误。

对我来说有点奇怪。为什么迁移是在保存时执行,而不是在打开文件时执行? 轻量级迁移已经在第 3 步完成了?

编辑:

看来migratePersistentStore这个名字并不代表从旧版本迁移到新版本。这似乎意味着从具有旧 URL 又名文件名和/或文件格式的旧持久存储迁移到具有新 URL 和/或文件格式的新持久存储。所以当另存为...或保存已经保存在自动保存目录中的新文档时会调用它。

【讨论】:

  • 你不需要第2步。创建一个新文档,做一些突变,将应用程序放在后台->自动保存,保存文档,错误。
  • 我是否正确理解数据迁移可能是问题所在?在最新的数据模型版本中不会创建新文档吗?
  • Willeke 和 Kneup 巫师,谢谢!你们俩都是对的。我误解了。保存到另一个文件时会发生错误。顺便说一句,这是非常基本的要求。解决方案应该已经到处可用了,但我还不知道。
【解决方案4】:

你能试试这个讨厌的解决方法吗?

class CustomPersistentStoreCoordinator : NSPersistentStoreCoordinator {

  static var m1 : Method? = nil
  static var m2 : Method? = nil

  override func migratePersistentStore(_ store: NSPersistentStore, to URL: URL, options: [AnyHashable : Any]? = nil, withType storeType: String) throws -> NSPersistentStore {
    var opt: [AnyHashable : Any] = options ?? [:]

    if #available(OSX 10.13, *) {
      opt[NSBinaryStoreSecureDecodingClasses] = NSSet(array: [ NSColor.self ])
    }

    let m1 = CustomPersistentStoreCoordinator.m1!
    let m2 = CustomPersistentStoreCoordinator.m2!
    method_exchangeImplementations(m2, m1)

    let x = try self.migratePersistentStore(store, to: URL, options: opt, withType: storeType)

    method_exchangeImplementations(m1, m2)
    return x
  }
}

class Document: NSPersistentDocument {

  override init() {
    super.init()

    let s1 = #selector(NSPersistentStoreCoordinator.migratePersistentStore(_:to:options:withType:))
    let s2 = #selector(CustomPersistentStoreCoordinator.migratePersistentStore(_:to:options:withType:))
    let m1 = class_getInstanceMethod(NSPersistentStoreCoordinator.self, s1)!
    let m2 = class_getInstanceMethod(CustomPersistentStoreCoordinator.self, s2)!
    CustomPersistentStoreCoordinator.m1 = m1
    CustomPersistentStoreCoordinator.m2 = m2
    method_exchangeImplementations(m1, m2)
  }

  override func configurePersistentStoreCoordinator(for url: URL, ofType fileType: String, modelConfiguration configuration: String?, storeOptions: [String : Any]? = nil) throws {
    // keep yours
  }

}

【讨论】:

  • 哇。这确实是一段令人讨厌的代码。但它确实起到了作用。然而,这有点令人难以置信。因此,与其(以某种方式)更改堆栈以使用您自己的 NSPersistentStoreCoordinator 版本,不如更改类结构本身。好的,所以我们调用新函数。但是那么在函数中,如果你把它切换回来,你调用self.migragePersistentStore,我们不是又递归调用了同一个函数吗?尊重。干得好!
  • 我想我明白了。自我是指原始商店。当然。因此,您调用原始函数。聪明的。我什至不知道有人可以做这些事情。你知道你的东西,托拉
  • 是的,我也是这么想的!递归调用?但是self.migratePersistentStoresuper.migratePersistentStore 的使用并没有区别。我需要考虑 Objective-C 的底层实现,包括在子类和超类中调用方法。
  • 我使用extension 发布了另一个解决方法。这个版本更容易理解。
猜你喜欢
  • 2013-08-02
  • 1970-01-01
  • 2020-05-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多