Cacau Core Data maneira eficiente de contar entidades

174

Eu li muito sobre o Core Data ... mas qual é uma maneira eficiente de fazer uma contagem por um tipo de entidade (como o SQL pode fazer com o SELECT count (1) ...). Agora acabei de resolver esta tarefa selecionando todos com NSFetchedResultsControllere obtendo a contagem do NSArray! Tenho certeza de que essa não é a melhor maneira ...

erazorx
fonte

Respostas:

303

Não sei se o uso de NSFetchedResultsController é a maneira mais eficiente de atingir seu objetivo (mas pode ser). O código explícito para obter a contagem de instâncias da entidade está abaixo:

// 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
fonte
1
No Leopard, você deseja usar countForFetchRequest: e não executeFetchRequest:
IlDan
E pule para definir o predicado. Nenhum predicado: obter todos os objetos que correspondem à descrição entidade
IlDan
4
Apenas para sua informação, conte == 0 se não houver resultados para a solicitação específica, NSNotFound = NSIntegerMax, portanto '// Erro de Handel' não será executado se não houver resultados.
Intentss
Existe um erro de digitação em: setIncludesSubentities? Acho que a documentação indica uma letra minúscula "e" em "entidades" em vez da letra maiúscula "E" no código de exemplo.
Mike
2
@LarsSchneider a documentação para os countForFetchRequest:error:estados NSNotFoundretornados em caso de erro. Em geral, o NSErrormanuseio na convenção de cacau é que o valor de erré indefinido (e geralmente perigoso) se nenhum erro ocorrer.
Barry Wark
61

Para ser claro, você não está contando entidades, mas instâncias de uma entidade específica. (Para contar literalmente as entidades, solicite ao modelo de objeto gerenciado a contagem de suas entidades.)

Para contar todas as instâncias de uma determinada entidade sem buscar todos os dados, use -countForFetchRequest:.

Por exemplo:

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

Rápido

É bastante fácil obter uma contagem do número total de instâncias de uma entidade no Core Data:

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

Eu testei isso no simulador com uma contagem de mais de 400.000 objetos e o resultado foi bastante rápido (embora não instantâneo).

Suragch
fonte
23

Vou acrescentar isso para torná-lo ainda mais eficiente ... e, como é apenas uma contagem, você realmente não precisa de nenhum valor de propriedade e, certamente, como um dos exemplos de código acima, também não precisa de subentidades.

Portanto, o código deve ser assim:

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 que ajude.

Oscar Salguero
fonte
10

Acredito que a maneira mais fácil e eficiente de contar objetos é definir o NSFetchRequesttipo de resultado NSCountResultTypee executá-lo com o 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
fonte
6

Eu escrevi um método utilitário simples para o Swift 3 buscar a contagem dos 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
fonte
3

Em 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
fonte
1

É realmente apenas isso:

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

"Barco" é apenas o nome da entidade na tela do seu modelo de dados:

insira a descrição da imagem aqui

O que é global yourContainer?

Para usar os dados principais, em algum momento do seu aplicativo, apenas uma vez, basta acessar

var yourContainer = NSPersistentContainer(name: "stuff")

onde "material" é simplesmente o nome do arquivo do modelo de dados.

insira a descrição da imagem aqui

Você simplesmente teria um singleton para isso,

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

Então, de qualquer lugar do aplicativo

core.container

é o seu contêiner,

Portanto, na prática, para obter a contagem de qualquer entidade, é apenas

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

Se você deseja encontrar a contagem para busca predicada específica, acredito que seja a melhor maneira:

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

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