开发者

Fastest way to check if an object exists in Core Data or not?

I want to see if an object is persisted in Core Data or not. For example, I have Friends in Core Data, and I identify them by firstName. I can query core data to see if "George" is known. If the re开发者_如何学编程sult set array contains more than zero objects, I know George is there. But Core Data loads the whole thing into memory, and I actually just want to know if George is stored or not.

How would I do it the most efficient way?


Setup a Core Data request and, instead of actually issuing the query, do the following:

NSError *error = nil;
NSUInteger count = [managedObjectContext countForFetchRequest:request
                                                        error:&error];
if (!error) {
    return count;
} else {
  return 0;
}

In practice, the method countForFetchRequest:error: returns the number of objects a given fetch request would have returned if it had been passed to executeFetchRequest:error:.


Edit: (by Regexident)

As Josh Caswell correctly commented, the correct way to handle errors is either this:

if (count == NSNotFound) {
    NSLog(@"Error: %@", error);
    return 0;
}
return count;

or this (without error logging):

return (count != NSNotFound) ? count : 0;


Yes, definitely there is a better method. Setup a fetch request as usual, but, instead of actually executing it, simply ask for the number of objects it would have returned if it had been passed to executeFetchRequest:error:

This can be done using

- (NSUInteger)countForFetchRequest:(NSFetchRequest *)request error:(NSError **)error;

Something like this:

- (int) numberOfContacts{

    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    NSManagedObjectContext *managedObjectContext = yourManagedObjectContext;
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Contacts" inManagedObjectContext:managedObjectContext];
    [request setEntity:entity];

    NSError *error = nil;
    NSUInteger count = [managedObjectContext countForFetchRequest:request error:&error];
    [request release];

    if (!error){
        return count;
    }
    else
        return -1;

}


Update to SWIFT 5:

func checkIfItemExist(id: Int, type: String) -> Bool {

    let managedContext = CoreDataStack.sharedInstance.persistentContainer.viewContext
    let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "DetailsEntity")
    fetchRequest.fetchLimit =  1
    fetchRequest.predicate = NSPredicate(format: "id == %d" ,id)
    fetchRequest.predicate = NSPredicate(format: "type == %@" ,type)

    do {
        let count = try managedContext.count(for: fetchRequest)
        if count > 0 {
            return true
        }else {
            return false
        }
    }catch let error as NSError {
        print("Could not fetch. \(error), \(error.userInfo)")
        return false
    }
}


If the goal is to check if the object exist the solution is to implement this method in your Friend Model:

-(BOOL)exist
{
    NSManagedObjectContext *managedObjectContext = yourManagedObjectContext;
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Friends" inManagedObjectContext:managedObjectContext];

    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setEntity:entity];
    [request setFetchLimit:1];
    [request setPredicate:[NSPredicate predicateWithFormat:@"firstName == %@", self.firstName]];    

    NSError *error = nil;
    NSUInteger count = [managedObjectContext countForFetchRequest:request error:&error];

    if (count)
    {
        return YES;
    }
    else
    {
        return NO;
    }
}


According to Core Data Documentation, you should not keep fetching to see if objects exists.

There are many situations where you may need to find existing objects (objects already saved in a store) for a set of discrete input values. A simple solution is to create a loop, then for each value in turn execute a fetch to determine whether there is a matching persisted object and so on. This pattern does not scale well. If you profile your application with this pattern, you typically find the fetch to be one of the more expensive operations in the loop (compared to just iterating over a collection of items). Even worse, this pattern turns an O(n) problem into an O(n^2) problem.

Edit March 16:
I am not a db expert, but since people are asking for a more efficient solution, consider this set:

set1 = [apple, orange, banana, pineapple, lettuce]

We want to find out if [mango, apple, grape] is a part of this set.

The docs tell us not to iterate through [mango, apple, grape] and query the database looking for each item in turn because this is slow.

Consider this solution:

Hash the sets on the server side:

hash([mango, apple, grape]) = 234095dda321affe...

You can then bypass Core Data completely by asking the server if anything changed. If the sets are different, you can then dump the objects in a managed object context and do a bulk save.

If you were really looking to see if each object in turn was a part of the set, you could do a fetch based on an indexed characteristic, such as "fruit with skin".

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜