My Core Data基于文档的应用程序在"另存为"时崩溃。这个问题似乎类似于可可开发线程中描述的问题,标题为"NSPersistentDocument objects"在10.9中的Duplicate,Rename之后被删除
关键区别在于:
- 我的目标是在OS X 10.10 Yosemite上运行
- 使用"另存为"而不是重复
- 崩溃发生得更早。MOC保存期间
该问题甚至会影响最简单的NSPersistentDocument。它至少自2014年以来就一直存在。因此,我希望其他人也遇到同样的问题,并有一个你愿意分享的解决方法。
我的示例项目使用具有单个属性的单个实体。它有一个表视图来显示实体的所有实例,还有一个按钮来创建一个新实例。我偏离了默认模板,只是禁用了autosavesInPlace。
再现崩溃的步骤是:
- 在约塞米蒂建造并运行。该错误似乎已在El中修复Capitan
- 创建新文档
- 插入新对象
- 保存文档
- 关闭文档
- 重新打开文档
- 更改表中属性的值
- 使用"另存为"以新名称保存
在OS X Yosemite上,这总是与以下回溯一起崩溃:
_propertyAtIndexForEntityDescription ()
snapshot_get_value_as_object ()
-[NSManagedObject(_NSInternalMethods) _validatePropertiesWithError:] ()
-[NSManagedObject(_NSInternalMethods) _validateForSave:] ()
-[NSManagedObject validateForUpdate:] ()
-[NSManagedObjectContext(_NSInternalAdditions) _validateObjects:forOperation:error:exhaustive:forSave:] ()
-[NSManagedObjectContext(_NSInternalAdditions) _validateChangesForSave:] ()
-[NSManagedObjectContext(_NSInternalChangeProcessing) _prepareForPushChanges:] ()
-[NSManagedObjectContext save:] ()
-[NSPersistentDocument writeToURL:ofType:forSaveOperation:originalContentsURL:error:] ()
-[NSDocument _writeSafelyToURL:ofType:forSaveOperation:forceTemporaryDirectory:error:] ()
-[NSDocument _writeSafelyToURL:ofType:forSaveOperation:error:] ()
-[NSDocument writeSafelyToURL:ofType:forSaveOperation:error:] ()
-[NSPersistentDocument writeSafelyToURL:ofType:forSaveOperation:error:] ()
__66-[NSDocument saveToURL:ofType:forSaveOperation:completionHandler:]_block_invoke_22353 ()
__66-[NSDocument saveToURL:ofType:forSaveOperation:completionHandler:]_block_invoke2350 ()
__66-[NSDocument saveToURL:ofType:forSaveOperation:completionHandler:]_block_invoke_22222 ()
__110-[NSFileCoordinator(NSPrivate) _coordinateReadingItemAtURL:options:writingItemAtURL:options:error:byAccessor:]_block_invoke428 ()
-[NSFileCoordinator(NSPrivate) _invokeAccessor:orDont:andRelinquishAccessClaim:] ()
-[NSFileCoordinator(NSPrivate) _coordinateReadingItemAtURL:options:writingItemAtURL:options:error:byAccessor:] ()
-[NSDocument _fileCoordinator:coordinateReadingContentsAndWritingItemAtURL:byAccessor:] ()
-[NSDocument _fileCoordinator:asynchronouslyCoordinateReadingContentsAndWritingItemAtURL:byAccessor:] ()
__66-[NSDocument saveToURL:ofType:forSaveOperation:completionHandler:]_block_invoke2221 ()
-[NSDocument _prepareToSaveToURL:forSaveOperation:completionHandler:] ()
__66-[NSDocument saveToURL:ofType:forSaveOperation:completionHandler:]_block_invoke ()
-[NSDocument continueFileAccessUsingBlock:] ()
-[NSDocument _performFileAccessOnMainThread:usingBlock:] ()
-[NSDocument performAsynchronousFileAccessUsingBlock:] ()
-[NSDocument saveToURL:ofType:forSaveOperation:completionHandler:] ()
__85-[NSDocument saveToURL:ofType:forSaveOperation:delegate:didSaveSelector:contextInfo:]_block_invoke_2 ()
-[NSDocument _commitEditingThenContinue:] ()
__62-[NSPersistentDocument _documentEditor:didCommit:withContext:]_block_invoke ()
编辑1。可能的解决方法:
我可以通过防止在"另存为"操作期间保存原始托管对象上下文来修补崩溃。"另存为"后,我立即关闭现有文档,并从新位置重新打开该文档。这一切都非常丑陋,可能会破坏其他NSPersistentDocument行为。
编辑2。上述解决方法丢失未保存的更改
阻止保存原始托管对象上下文可以避免崩溃。但是,最终结果是处于上次保存状态的文档的副本。未保存的更改将丢失。
编辑3。删除的快照
当旧的托管对象上下文尝试将更改保存到新文件时,对象快照不再知道其实体<_CDSnapshot_Entity_: 0x600001f3cfd0> (entity: (null); id: 0x40000b <x-coredata://83B64FD3-B5B9-44CB-976D-54C0326FDFF5/Entity/p1> ; data: (null))
。我看不到任何支持-[_CDSnapshot entity]
的实例变量。我认为它应该从对象ID中找到。
我提出了一个似乎适用于我的用例的变通方法。
- (BOOL)writeToURL:(NSURL *)absoluteURL
ofType:(NSString *)typeName
forSaveOperation:(NSSaveOperationType)saveOperation
originalContentsURL:(NSURL *)absoluteOriginalContentsURL
error:(NSError **)error
{
if ((saveOperation == NSSaveAsOperation) && (absoluteOriginalContentsURL != nil)) {
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager copyItemAtURL:absoluteOriginalContentsURL toURL:absoluteURL error:error]) {
return NO;
}
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
NSPersistentStoreCoordinator *persistentStoreCoordinator = managedObjectContext.persistentStoreCoordinator;
NSPersistentStore *store = [persistentStoreCoordinator persistentStoreForURL:absoluteOriginalContentsURL];
[persistentStoreCoordinator setURL:absoluteURL forPersistentStore:store];
if (![managedObjectContext save:error]) {
return NO;
}
return YES;
}
return [super writeToURL:absoluteURL
ofType:typeName
forSaveOperation:saveOperation
originalContentsURL:absoluteOriginalContentsURL
error:error];
}
另存为时,我将旧文档复制到新的(临时)位置。然后,我在持久存储上设置新的URL,并让托管对象上下文保存对该新文档的挂起的更改。
NSPersistentDocument负责生成作为absoluteURL
传入的临时URL,将保存的文件移动到新位置,并在保存完成后调用setFileURL:
。
我已经在支持文档的SQLite存储上禁用了日志记录。因此,我只需要在absoluteOriginalContentsURL
复制一个文件。