Forma eficiente de Cocoa Core Data para contar entidades

174

Leí mucho sobre Core Data ... pero cuál es una forma eficiente de hacer un recuento sobre un tipo de entidad (como SQL puede hacer con SELECT count (1) ...). ¡Ahora acabo de resolver esta tarea seleccionando todo NSFetchedResultsControllery obteniendo el recuento de NSArray! Estoy seguro de que esta no es la mejor manera ...

erazorx
fuente

Respuestas:

303

No sé si usar NSFetchedResultsController es la forma más eficiente de lograr su objetivo (pero puede serlo). El código explícito para obtener el recuento de instancias de entidad está a continuación:

// 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
fuente
1
En Leopard desea usar countForFetchRequest: y no ejecutarFetchRequest:
IlDan el
Y omita para establecer el predicado. Sin predicado: conseguir todos los objetos que coinciden con la descripción entidad
IlDan
44
Solo para su información, cuente == 0 si no hay resultados para la solicitud específica, NSNotFound = NSIntegerMax, por lo que '// Error de Handel' no se ejecutará si no hay resultados.
Intentss
¿Hay un error tipográfico en: setIncludesSubentities? Creo que la documentación indica una minúscula "e" en "entidades" en lugar de la mayúscula "E" en el código de ejemplo.
Mike
2
@LarsSchneider la documentación de los countForFetchRequest:error:estados que NSNotFoundse devuelve en caso de error. En general, el NSErrormanejo en la convención del cacao es que el valor de errno está definido (y a menudo es peligroso) si no se produce ningún error.
Barry Wark
61

Para ser claros, no estás contando entidades, sino instancias de una entidad particular. (Para contar literalmente las entidades, solicite al modelo de objeto administrado el recuento de sus entidades).

Para contar todas las instancias de una entidad determinada sin obtener todos los datos, utilice -countForFetchRequest:.

Por ejemplo:

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
fuente
32

Rápido

Es bastante fácil obtener un recuento del número total de instancias de una entidad en Core Data:

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

Probé esto en el simulador con más de 400,000 recuentos de objetos y el resultado fue bastante rápido (aunque no instantáneo).

Suragch
fuente
23

Solo agregaré eso para hacerlo aún más eficiente ... y debido a que es solo un recuento, realmente no necesita ningún valor de propiedad y, ciertamente, como uno de los ejemplos de código anteriores, tampoco necesita subentidades.

Entonces, el código debería ser así:

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;
}

Espero eso ayude.

Oscar Salguero
fuente
10

Creo que la forma más fácil y eficiente de contar objetos es establecer el NSFetchRequesttipo de resultado NSCountResultTypey ejecutarlo con el NSManagedObjectContext countForFetchRequest:error:método.

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
fuente
6

Escribí un método de utilidad simple para Swift 3 para obtener el recuento de los objetos.

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
fuente
3

En 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
fuente
1

Realmente es solo esto:

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

"Barco" es solo el nombre de la entidad de la pantalla de su modelo de datos:

ingrese la descripción de la imagen aquí

¿Qué es lo global yourContainer?

Para usar datos básicos, en algún momento de su aplicación, solo una vez, simplemente vaya

var yourContainer = NSPersistentContainer(name: "stuff")

donde "cosas" es simplemente el nombre del archivo del modelo de datos.

ingrese la descripción de la imagen aquí

Simplemente tendrías un singleton para esto,

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)") }
        }
    }
}

Entonces, desde cualquier lugar de la aplicación

core.container

es tu contenedor

Entonces, en la práctica, para obtener el recuento de cualquier entidad, es solo

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

Si desea encontrar el recuento para la búsqueda predicada específica, creo que esta es la mejor manera:

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

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