Cocoa Core Data moyen efficace de compter les entités

174

J'ai beaucoup lu sur les données de base ... mais quel est un moyen efficace de faire un compte sur un type d'entité (comme SQL peut le faire avec SELECT count (1) ...). Maintenant, je viens de résoudre cette tâche en sélectionnant tout avec NSFetchedResultsControlleret en obtenant le nombre de NSArray! Je suis sûr que ce n'est pas la meilleure façon ...

Erazorx
la source

Réponses:

303

Je ne sais pas si l'utilisation de NSFetchedResultsController est le moyen le plus efficace d'atteindre votre objectif (mais c'est peut-être le cas). Le code explicite pour obtenir le nombre d'instances d'entité est ci-dessous:

// assuming NSManagedObjectContext *moc

NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:entityName inManagedObjectContext:moc]];

[request setIncludesSubentities:NO]; //Omit subentities. Default is YES (i.e. include subentities)

NSError *err;
NSUInteger count = [moc countForFetchRequest:request error:&err];
if(count == NSNotFound) {
  //Handle error
}

[request release];
Barry Wark
la source
1
Sur Leopard, vous voulez utiliser countForFetchRequest: et non executeFetchRequest:
IlDan
Et sautez pour définir le prédicat. Pas de prédicat: obtenez tous les objets qui correspondent à la description de l'entité
IlDan
4
Juste pour info, count == 0 s'il n'y a pas de résultats pour la requête spécifique, NSNotFound = NSIntegerMax, donc '// Handel error' ne sera pas exécuté s'il n'y a pas de résultats.
Intentss
Y a-t-il une faute de frappe sur: setIncludesSubentities? Je pense que la documentation indique un «e» minuscule dans «entités» plutôt que le «E» majuscule dans l'exemple de code.
Mike
2
@LarsSchneider la documentation des countForFetchRequest:error:états qui NSNotFoundest renvoyée en cas d'erreur. En général, la NSErrormanipulation dans la convention Cocoa est que la valeur de errest indéfinie (et souvent dangereuse) si aucune erreur ne se produit.
Barry Wark
61

Pour être clair, vous ne comptez pas les entités, mais les instances d'une entité particulière. (Pour compter littéralement les entités, demandez au modèle d'objet géré le nombre de ses entités.)

Pour compter toutes les instances d'une entité donnée sans récupérer toutes les données, utilisez -countForFetchRequest:.

Par exemple:

NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity: [NSEntityDescription entityForName: entityName inManagedObjectContext: context]];

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

[request release];

return count;
Jim Correia
la source
32

Rapide

Il est assez facile d'obtenir un décompte du nombre total d'instances d'une entité dans Core Data:

let context = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "MyEntity")
let count = context.countForFetchRequest(fetchRequest, error: nil)

J'ai testé cela dans le simulateur avec plus de 400 000 objets et le résultat était assez rapide (mais pas instantané).

Suragch
la source
23

J'ajouterai simplement cela pour le rendre encore plus efficace ... et parce que c'est juste un décompte, vous n'avez pas vraiment besoin de valeur de propriété et certainement comme l'un des exemples de code ci-dessus, vous n'avez pas besoin de sous-entités non plus.

Donc, le code devrait être comme ceci:

int entityCount = 0;
NSEntityDescription *entity = [NSEntityDescription entityForName:@"YourEntity" inManagedObjectContext:_managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:entity];
[fetchRequest setIncludesPropertyValues:NO];
[fetchRequest setIncludesSubentities:NO];
NSError *error = nil;
NSUInteger count = [_managedObjectContext countForFetchRequest: fetchRequest error: &error];
if(error == nil){
    entityCount = count;
}

J'espère que ça aide.

Oscar Salguero
la source
10

Je pense que le moyen le plus simple et le plus efficace de compter les objets est de définir le NSFetchRequesttype de résultat NSCountResultTypeet de l'exécuter avec NSManagedObjectContext countForFetchRequest:error:méthode.

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:entityName];
fetchRequest.resultType = NSCountResultType;
NSError *fetchError = nil;
NSUInteger itemsCount = [managedObjectContext countForFetchRequest:fetchRequest error:&fetchError];
if (itemsCount == NSNotFound) {
    NSLog(@"Fetch error: %@", fetchError);
}

// use itemsCount
Yuriy Pavlyshak
la source
6

J'ai écrit une méthode utilitaire simple pour Swift 3 pour récupérer le nombre d'objets.

static func fetchCountFor(entityName: String, predicate: NSPredicate, onMoc moc: NSManagedObjectContext) -> Int {

    var count: Int = 0

    moc.performAndWait {

        let fetchRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: entityName)
        fetchRequest.predicate = predicate
        fetchRequest.resultType = NSFetchRequestResultType.countResultType

        do {
            count = try moc.count(for: fetchRequest)
        } catch {
            //Assert or handle exception gracefully
        }

    }

    return count
}
Jarora
la source
3

Dans Swift 3

  static func getProductCount() -> Int {
    let moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
    let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Product")
    let count = try! moc.count(for: fetchRequest)
    return count
}
Philippe H. Regenass
la source
1

C'est vraiment juste ceci:

let kBoat = try? yourContainer.viewContext.count(for: NSFetchRequest(entityName: "Boat"))

"Bateau" est simplement le nom de l'entité sur votre écran de modèle de données:

entrez la description de l'image ici

Qu'est-ce que le global yourContainer?

Pour utiliser les données de base, à un moment donné de votre application, une seule fois, il vous suffit d'aller

var yourContainer = NSPersistentContainer(name: "stuff")

où «stuff» est simplement le nom du fichier de modèle de données.

entrez la description de l'image ici

Vous auriez simplement un singleton pour ça,

import CoreData
public let core = Core.shared
public final class Core {
    static let shared = Core()
    var container: NSPersistentContainer!
    private init() {
        container = NSPersistentContainer(name: "stuff")
        container.loadPersistentStores { storeDescription, error in
            if let error = error { print("Error loading... \(error)") }
        }
    }
    
    func saveContext() {
        if container.viewContext.hasChanges {
            do { try container.viewContext.save()
            } catch { print("Error saving... \(error)") }
        }
    }
}

Donc de n'importe où dans l'application

core.container

est votre conteneur,

Donc, dans la pratique, pour obtenir le décompte de n'importe quelle entité, c'est juste

let k = try? core.container.viewContext.count(for: NSFetchRequest(entityName: "Boat"))
Fattie
la source
0

Si vous voulez trouver le compte pour une récupération prédiquée spécifique, je pense que c'est le meilleur moyen:

NSError *err;
NSUInteger count = [context countForFetchRequest:fetch error:&err];

if(count > 0) {
NSLog(@"EXIST"); 
} else {
NSLog(@"NOT exist");
}
Umit Kaya
la source