Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/58.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Core data 如何判断“NSManagedObject”是否已被删除?_Core Data_Ios_Nsmanagedobject - Fatal编程技术网

Core data 如何判断“NSManagedObject”是否已被删除?

Core data 如何判断“NSManagedObject”是否已被删除?,core-data,ios,nsmanagedobject,Core Data,Ios,Nsmanagedobject,我有一个已删除的NSManagedObject,包含该托管对象的上下文已保存。我知道isDeleted返回YES如果核心数据将在下一次保存操作期间要求持久存储删除对象。但是,由于保存已经发生,isDeleted返回NO 保存NSManagedObject的包含上下文后,如何判断该对象是否已被删除 (如果您想知道,为什么引用已删除托管对象的对象尚未意识到删除,这是因为删除和上下文保存是由后台线程启动的,该线程使用performSelectorOnMainThread:withObject:wait

我有一个已删除的
NSManagedObject
,包含该托管对象的上下文已保存。我知道
isDeleted
返回
YES
如果核心数据将在下一次保存操作期间要求持久存储删除对象。但是,由于保存已经发生,
isDeleted
返回
NO

保存
NSManagedObject
的包含上下文后,如何判断该对象是否已被删除


(如果您想知道,为什么引用已删除托管对象的对象尚未意识到删除,这是因为删除和上下文保存是由后台线程启动的,该线程使用
performSelectorOnMainThread:withObject:waitUntilDone:
执行删除和保存操作)检查托管对象的上下文似乎有效:

if (managedObject.managedObjectContext == nil) {
    // Assume that the managed object has been deleted.
}
来自苹果公司关于
managedObjectContext
的文档

如果 接收器已从其目录中删除 上下文

如果接收器出现故障,则呼叫 此方法不会导致它起火

这两个似乎都是好事


更新:如果您试图测试是否已删除专门使用
objectWithID:
检索的托管对象,请签出。他指出,如果您使用已删除对象的ID调用
objectWithID:
,则返回的对象将是一个未将其
managedObjectContext
设置为nil的错误。因此,您不能简单地检查它的
managedObjectContext
来测试它是否已被删除。如果可以,请使用ID:error:现有对象。如果不是,例如,您的目标是Mac OS 10.5或iOS 2.0,则需要执行其他操作来测试删除。有关详细信息,请参见。

更新:一个改进的答案,基于下面讨论中的詹姆斯·哈德尔斯顿的想法

- (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是否已从持久存储中删除,但它并不总是像苹果在其文档中所说的那样准确:

如果接收者已从其数据库中删除,此方法可能返回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.

如您所见,
-managedObjectContext
如果NSManagedbject已从持久性存储中删除,则不会始终返回nil。

由于我最近在iOS应用程序中实现iCloud的经验,该应用程序依赖于核心数据来实现持久性,我意识到最好的方法是观察框架的通知。至少,这比依赖一些模糊的方法要好,这些方法可能会,也可能不会告诉您是否删除了某个托管对象

对于“纯”核心数据应用程序,您应该在主线程上观察NSManagedObjectContextObjectsIDChangeNotification。通知的用户信息字典包含已插入、删除和更新的托管对象objectID集合

如果您在其中一个集合中找到托管对象的objectID,那么您可以以某种好的方式更新应用程序和UI


就这样。。。欲了解更多信息,请参阅苹果的核心数据编程指南《与核心数据并发》一章。有一节“使用通知跟踪其他线程中的更改”,但不要忘记检查前一节“使用线程限制支持并发”。

我担心其他答案中的讨论实际上隐藏了正确答案的简单性。在几乎所有情况下,正确答案都是:

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

  • 在Swift 3,Xcode 7.3中验证

    您还可以简单地
    打印每个上下文的内存引用并检查

    (a) if the context exists,
    (b) if the contexts of 2 objects are different
    
    例如:(书和成员是两个不同的对象)

    如果上下文存在但不同,它会打印类似的内容

    0x7fe758c307d0
    0x7fe758c15d70
    

    有趣的是,虽然看起来这对没有属性的对象不起作用。另外,为什么不使用
    existingObjectWithID:error:
    而不是
    objectWithID:
    ,只需检查返回值是否等于
    nil
    ?啊,你是对的,
    -existingObjectWithID:error:
    是一种更好的方法!)我写的答案是与MacOSX10.5+兼容,所以我忽略了这个方法,它只有10.6+版本。是的,我的答案不适用于没有任何属性的对象,尽管数据模型中不太可能有空对象。你是对的。对象不太可能没有包含关系的属性。出于某种原因,我只考虑属性。隐马尔可夫模型。。。是否有一种方法可以在不检查所有属性的情况下快速评估ID为
    的objectWithID:
    返回的故障?(对于尚未删除的对象,访问每个属性的代价可能会很高。)如果有一个方法会触发错误,您可以对
    objectWithID:
    返回的对象调用该方法,以查看它是否确实存在。我寻找这样一种方法,但没有发现任何明显的问题。我想更好的优化方法是只查询一个
    0x7fe758c307d0
    0x7fe758c15d70