问题描述
我在保存NSPersistentDocument时遇到奇怪的行为。我可以创建一个自动保存的新文档。但是当我保存它时,调用了 write(to:ofType:for:absoluteOriginalContentsURL:)
,但它变成了 configurePersistentStoreCoordinator(for:ofType:modelConfiguration: storeOptions:)
没有被调用。不幸的是,我需要配置存储。原因是我需要将NSColor注册为可解码的 options [NSBinaryStoreSecureDecodingClasses] = NSSet(对象:NSColor.self)
。
I experience an odd behaviour regarding saving an NSPersistentDocument. I can create a new document which is autosaved without an issue. But when I save it write(to: ofType: for: absoluteOriginalContentsURL:)
is called but it turns to that configurePersistentStoreCoordinator(for: ofType: modelConfiguration: storeOptions:)
isn't called. That is unfortunate as I need to configure the store. Reason is that I need to register NSColor as decodable options[NSBinaryStoreSecureDecodingClasses] = NSSet(object: NSColor.self)
.
最初的尝试是自己叫它,但是那没带我到任何地方。我自己叫它并没有按预期注册设置。
First attempt was to call it myself, but that didn't lead me anywhere. Calling it myself didn't register the settings as expected.
NSPersistentDocument中的代码如下:
Code in my NSPersistentDocument looks like this:
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)")
}
}
恢复lt是以下输出(第二个configurePersistentStoreCoordinator输出在通过菜单命令另存为之后开始):
Result is the following output (second configurePersistentStoreCoordinator output is starting after the save as through menu command):
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
)}'.
以下代码有一个错误。请参考下面引用的错误修复版本。
The following code has a bug. Please refer to the bug fixed version cited below.
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
。
This workaround is going to be a practical solution until they enhance NSPersistentDocument.write(to...)
to take into account of options or they implement other means to handle options. Those options will be given to NSPersistentStoreCoordinator.migratePersistentStore(...)
and then be used to instantiate NSPersistentStore
.
错误的固定版本:
上述解决方法中存在一个错误。打开文档文件,创建新文件,进行更改,等待自动保存的文档等待30秒,关闭它们和/或将它们随机保存时,都会引起初始错误。
There was a bug in the preceding workaround. Opening document files, creating new files, making changes, waiting for thirty second to the document being auto-saved, closing them, and/or save-as-ing them randomly would cause the initial error. Reported by Wizard of Kneup.
这里是一个固定的版本,使用单例来确保仅应用一次毛毛雨。
Here is a fixed version using singleton to make sure swizzling is applied only once.
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
}
}
参考:
- Lazy Stored Properties
这篇关于未为saveAs NSPersistentDocument调用configurePersistentStoreCoordinator的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!