我有一个已删除的NSManagedObject
,并且已保存包含该托管对象的上下文。我理解,如果Core Data要求持久存储在下一次保存操作期间删除该对象,isDeleted
将返回YES
。但是,由于保存已经发生,isDeleted
会返回NO
。
在保存其包含的上下文后,判断NSManagedObject
是否已被删除的好方法是什么?
(如果您想知道为什么引用已删除的托管对象的对象还不知道删除,那是因为删除和上下文保存是由后台线程启动的,后台线程执行删除并使用{{保存1}}。)
答案 0 :(得分:90)
检查托管对象的上下文似乎有效:
if (managedObject.managedObjectContext == nil) {
// Assume that the managed object has been deleted.
}
来自Apple关于managedObjectContext
的文档......
如果是,则此方法可能返回nil 接收器已被删除 上下文。
如果接收器是故障,请致电 这种方法不会引发它。
这些似乎都是好事。
更新:如果您尝试测试是否已删除使用objectWithID:
专门检索的托管对象,请查看Dave Gallagher's answer。他指出,如果使用已删除对象的ID调用objectWithID:
,则返回的对象将是不将其managedObjectContext
设置为nil的错误。因此,您不能简单地检查其managedObjectContext
以测试它是否已被删除。如果可以,请使用existingObjectWithID:error:
。如果没有,例如,您的目标是Mac OS 10.5或iOS 2.0,则需要执行其他操作来测试删除。有关详细信息,请参阅his answer。
答案 1 :(得分:39)
更新:根据 James 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
(e.g. 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
是一种“非常好”的方式来查看缓存/陈旧的NSManagedObject是否具有已从Persistent Store中删除,但它并不总是准确,因为Apple在其文档中声明:
如果接收器已从其中删除,则此方法可能返回nil 上下文。
什么时候不回零?如果您使用已删除的NSManagedObject的-objectID
获取不同的NSManagedObject,如下所示:
// 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,则-managedObjectContext
将不会始终返回nil。
答案 2 :(得分:27)
我担心其他答案中的讨论实际上隐藏了正确答案的简单性。在几乎所有情况下,正确的答案是:
if ([moc existingObjectWithID:object.objectID error:NULL])
{
// object is valid, go ahead and use it
}
此答案不适用的唯一情况是:
NSObjectInaccessibleException
,或者您可以使用object.isDeleted
)答案 3 :(得分:11)
由于我最近在我的iOS应用程序中实现依赖Core Data持久化的iCloud的经验,我意识到最好的方法是观察框架的通知。至少,比依赖一些模糊的方法更好,这些方法可能会或可能不会告诉你某个托管对象是否被删除。
对于纯粹的'您应该在主线程上观察核心数据应用 NSManagedObjectContextObjectsDidChangeNotification 。通知的用户信息字典包含具有管理对象的集合'已插入,删除和更新的objectID。
如果您在其中一个集合中找到了托管对象的objectID,那么您可以用一些不错的方式更新您的应用程序和UI。
它......为了获得更多信息,请给出Apple的核心数据编程指南,核心数据并发章节。有一个部分"使用通知和#34;跟踪其他主题中的更改,但不要忘记检查前一个"使用线程限制来支持并发"。
答案 4 :(得分:1)
尝试这种方法:
if (manageObject.deleted) {
// assume that the managed object has been deleted.
}
答案 5 :(得分:0)
在Swift 3,Xcode 7.3中验证
您也可以简单地PRINT
每个上下文的内存引用并检查
(a) if the context exists,
(b) if the contexts of 2 objects are different
例如:(书和成员是两个不同的对象)
print(book.managedObjectContext)
print(member.managedObjectContext)
如果上下文存在但是不同,它会打印出类似的内容
0x7fe758c307d0
0x7fe758c15d70