我怎么知道一个`NSManagedObject`是否被删除?
我有一个NSManagedObject
已被删除,并且包含该托pipe对象的上下文已被保存。 我明白,如果核心数据将要求持久性存储在下一次保存操作中删除对象,则返回YES
。 但是,由于保存已经发生, isDeleted
返回NO
。
什么是一个很好的方法来告诉NSManagedObject
是否已被删除,其中包含的上下文已被保存?
(如果你想知道为什么引用被删除的pipe理对象的对象还没有意识到删除,那是因为删除和上下文保存是由执行删除操作并使用performSelectorOnMainThread:withObject:waitUntilDone:
保存的后台线程启动的performSelectorOnMainThread:withObject:waitUntilDone:
。)
检查pipe理对象的上下文似乎工作:
if (managedObject.managedObjectContext == nil) { // Assume that the managed object has been deleted. }
从苹果的文件managedObjectContext
…
如果接收方已经从其上下文中删除,则此方法可能返回nil。
如果接收机出现故障,调用此方法不会导致它触发。
这两个似乎是好东西。
更新:如果你想testing是否使用objectWithID:
检索了一个托pipe对象objectWithID:
已经被删除了,请查看Dave Gallagher的答案 。 他指出,如果调用objectWithID:
使用已删除对象的ID,则返回的对象将是一个未将其managedObjectContext
设置为nil的错误。 因此,您不能简单地检查其managedObjectContext
以testing它是否已被删除。 使用existingObjectWithID:error:
如果可以。 如果没有,例如,您的目标是Mac OS 10.5或iOS 2.0,则需要执行其他操作来testing删除操作。 详情请见他的回答 。
更新:一个改进的答案,根据詹姆斯Huddleston的想法在下面的讨论。
- (BOOL)hasManagedObjectBeenDeleted:(NSManagedObject *)managedObject { /* Returns YES if |managedObject| has been deleted from the Persistent Store, or NO if it has not. NO will be returned for NSManagedObject's who have been marked for deletion (eg their -isDeleted method returns YES), but have not yet been commited to the Persistent Store. YES will be returned only after a deleted NSManagedObject has been committed to the Persistent Store. Rarely, an exception will be thrown if Mac OS X 10.5 is used AND |managedObject| has zero properties defined. If all your NSManagedObject's in the data model have at least one property, this will not be an issue. Property == Attributes and Relationships Mac OS X 10.4 and earlier are not supported, and will throw an exception. */ NSParameterAssert(managedObject); NSManagedObjectContext *moc = [self managedObjectContext]; // Check for Mac OS X 10.6+ if ([moc respondsToSelector:@selector(existingObjectWithID:error:)]) { NSManagedObjectID *objectID = [managedObject objectID]; NSManagedObject *managedObjectClone = [moc existingObjectWithID:objectID error:NULL]; if (!managedObjectClone) return YES; // Deleted. else return NO; // Not deleted. } // Check for Mac OS X 10.5 else if ([moc respondsToSelector:@selector(countForFetchRequest:error:)]) { // 1) Per Apple, "may" be nil if |managedObject| deleted but not always. if (![managedObject managedObjectContext]) return YES; // Deleted. // 2) Clone |managedObject|. All Properties will be un-faulted if // deleted. -objectWithID: always returns an object. Assumed to exist // in the Persistent Store. If it does not exist in the Persistent // Store, firing a fault on any of its Properties will throw an // exception (#3). NSManagedObjectID *objectID = [managedObject objectID]; NSManagedObject *managedObjectClone = [moc objectWithID:objectID]; // 3) Fire fault for a single Property. NSEntityDescription *entityDescription = [managedObjectClone entity]; NSDictionary *propertiesByName = [entityDescription propertiesByName]; NSArray *propertyNames = [propertiesByName allKeys]; NSAssert1([propertyNames count] != 0, @"Method cannot detect if |managedObject| has been deleted because it has zero Properties defined: %@", managedObject); @try { // If the property throws an exception, |managedObject| was deleted. (void)[managedObjectClone valueForKey:[propertyNames objectAtIndex:0]]; return NO; // Not deleted. } @catch (NSException *exception) { if ([[exception name] isEqualToString:NSObjectInaccessibleException]) return YES; // Deleted. else [exception raise]; // Unknown exception thrown. } } // Mac OS X 10.4 or earlier is not supported. else { NSAssert(0, @"Unsupported version of Mac OS X detected."); } }
旧的/贬低的答案:
我写了一个更好的方法。 self
是你的核心数据类/控制器。
- (BOOL)hasManagedObjectBeenDeleted:(NSManagedObject *)managedObject { // 1) Per Apple, "may" be nil if |managedObject| was deleted but not always. if (![managedObject managedObjectContext]) return YES; // Deleted. // 2) Clone |managedObject|. All Properties will be un-faulted if deleted. NSManagedObjectID *objectID = [managedObject objectID]; NSManagedObject *managedObjectClone = [[self managedObjectContext] objectWithID:objectID]; // Always returns an object. Assumed to exist in the Persistent Store. If it does not exist in the Persistent Store, firing a fault on any of its Properties will throw an exception. // 3) Fire faults for Properties. If any throw an exception, it was deleted. NSEntityDescription *entityDescription = [managedObjectClone entity]; NSDictionary *propertiesByName = [entityDescription propertiesByName]; NSArray *propertyNames = [propertiesByName allKeys]; @try { for (id propertyName in propertyNames) (void)[managedObjectClone valueForKey:propertyName]; return NO; // Not deleted. } @catch (NSException *exception) { if ([[exception name] isEqualToString:NSObjectInaccessibleException]) return YES; // Deleted. else [exception raise]; // Unknown exception thrown. Handle elsewhere. } }
正如James Huddleston在他的回答中所提到的那样,检查NSManagedObject的-managedObjectContext
返回nil
是一种非常好的方法,用来查看caching/陈旧的NSManagedObject是否已经从Persistent Store中删除,但是这并不总是准确的,文档:
如果接收方已经从其上下文中删除,则此方法可能返回nil。
什么时候不会返回零? 如果您使用删除的NSManagedObject的-objectID
获取不同的NSManagedObject, -objectID
所示:
// 1) Create a new NSManagedObject, save it to the Persistant Store. CoreData *coreData = ...; NSManagedObject *apple = [coreData addManagedObject:@"Apple"]; [apple setValue:@"Mcintosh" forKey:@"name"]; [coreData saveMOCToPersistentStore]; // 2) The `apple` will not be deleted. NSManagedObjectContext *moc = [apple managedObjectContext]; if (!moc) NSLog(@"2 - Deleted."); else NSLog(@"2 - Not deleted."); // This prints. The `apple` has just been created. // 3) Mark the `apple` for deletion in the MOC. [[coreData managedObjectContext] deleteObject:apple]; moc = [apple managedObjectContext]; if (!moc) NSLog(@"3 - Deleted."); else NSLog(@"3 - Not deleted."); // This prints. The `apple` has not been saved to the Persistent Store yet, so it will still have a -managedObjectContext. // 4) Now tell the MOC to delete the `apple` from the Persistent Store. [coreData saveMOCToPersistentStore]; moc = [apple managedObjectContext]; if (!moc) NSLog(@"4 - Deleted."); // This prints. -managedObjectContext returns nil. else NSLog(@"4 - Not deleted."); // 5) What if we do this? Will the new apple have a nil managedObjectContext or not? NSManagedObjectID *deletedAppleObjectID = [apple objectID]; NSManagedObject *appleClone = [[coreData managedObjectContext] objectWithID:deletedAppleObjectID]; moc = [appleClone managedObjectContext]; if (!moc) NSLog(@"5 - Deleted."); else NSLog(@"5 - Not deleted."); // This prints. -managedObjectContext does not return nil! // 6) Finally, let's use the method I wrote, -hasManagedObjectBeenDeleted: BOOL deleted = [coreData hasManagedObjectBeenDeleted:appleClone]; if (deleted) NSLog(@"6 - Deleted."); // This prints. else NSLog(@"6 - Not deleted.");
打印输出如下:
2 - Not deleted. 3 - Not deleted. 4 - Deleted. 5 - Not deleted. 6 - Deleted.
正如你所看到的,如果一个NSManagedObject已经从Persistent Store中删除, -managedObjectContext
不会总是返回nil。
我担心在其他答案中的讨论实际上隐藏了正确答案的简单性。 在几乎所有情况下,正确的答案是:
if ([moc existingObjectWithID:object.objectID error:NULL]) { // object is valid, go ahead and use it }
这个答案不适用的唯一情况是:
- 如果您的目标是Mac OS 10.5或更早的版本
- 如果你的目标是iOS 2.0或更早的版本
- 如果对象/上下文尚未保存(在这种情况下,您不关心,因为它不会抛出一个
NSObjectInaccessibleException
,或者您可以使用object.isDeleted
)
由于我最近在依靠Core Data进行持久化的iOS应用程序中实现iCloud的经验,我意识到最好的方法是观察框架的通知。 至less,比依靠一些模糊的方法更好,或者可能不会告诉你是否删除了一些被pipe理的对象。
对于“纯”核心数据应用程序,您应该在主线程上观察NSManagedObjectContextObjectsDidChangeNotification 。 通知的用户信息字典包含已插入,删除和更新的托pipe对象的对象标识集。
如果您在其中一个集合中find您的托pipe对象的objectID,那么您可以用一些很好的方式更新您的应用程序和UI。
就是这样……欲了解更多信息,请给苹果的核心数据编程指南,核心数据并发与核心章节的机会。 有一节“使用通知跟踪其他线程的变化”,但不要忘记检查前一个“使用线程约束来支持并发”。
试试这个方法:
if (manageObject.deleted) { // assume that the managed object has been deleted. }
在Swift 3,Xcode 7.3中validation
您也可以简单地PRINT
每个上下文的内存引用并检查
(a) if the context exists, (b) if the contexts of 2 objects are different
例如:(书和成员是2个不同的对象)
print(book.managedObjectContext) print(member.managedObjectContext)
如果上下文存在但是不同,它会打印这样的内容
0x7fe758c307d0 0x7fe758c15d70