Cocoa Core Data efektywny sposób liczenia podmiotów

174

Czytałem dużo o danych podstawowych ... ale jaki jest skuteczny sposób na obliczenie typu jednostki (jak SQL może zrobić z SELECT count (1) ...). Teraz właśnie rozwiązałem to zadanie, wybierając wszystko za pomocą NSFetchedResultsControlleri obliczając liczbę NSArray! Jestem pewien, że to nie jest najlepszy sposób ...

erazorx
źródło

Odpowiedzi:

303

Nie wiem, czy użycie NSFetchedResultsController jest najbardziej wydajnym sposobem osiągnięcia celu (ale może być). Poniżej znajduje się jawny kod, aby uzyskać liczbę wystąpień encji:

// 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
źródło
1
W Leopardzie chcesz użyć countForFetchRequest: i nie wykonaćFetchRequest:
IlDan.
I pomiń, aby ustawić predykat. Brak predykatu: pobierz wszystkie obiekty, które pasują do opisu jednostki
IlDan
4
Po prostu FYI, count == 0, jeśli nie ma wyników dla określonego żądania, NSNotFound = NSIntegerMax, więc „// Błąd handlu” nie zostanie wykonany, jeśli nie ma wyników.
Intencje
Czy w: setIncludesSubentities jest literówka? Myślę, że dokumentacja wskazuje małą literę „e” w „jednostkach” zamiast dużej litery „E” w przykładowym kodzie.
Mike,
2
@LarsSchneider dokumentacja dotycząca countForFetchRequest:error:stanów NSNotFoundzwracana w przypadku błędu. Ogólnie rzecz biorąc, NSErrorobsługa w konwencji kakaowej polega na tym, że wartość errjest nieokreślona (i często niebezpieczna), jeśli nie wystąpi żaden błąd.
Barry Wark
61

Żeby było jasne, nie liczysz jednostek, ale wystąpienia określonej jednostki. (Aby dosłownie policzyć jednostki, poproś zarządzany model obiektów o liczbę jego jednostek).

Aby policzyć wszystkie wystąpienia danej jednostki bez pobierania wszystkich danych, funkcja use -countForFetchRequest:.

Na przykład:

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
źródło
32

Szybki

Dość łatwo jest obliczyć całkowitą liczbę wystąpień podmiotu w danych podstawowych:

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

Przetestowałem to w symulatorze z liczbą obiektów ponad 400 000 i wynik był dość szybki (choć nie natychmiastowy).

Suragch
źródło
23

Dodam tylko, że aby uczynić go jeszcze bardziej wydajnym ... a ponieważ jest to tylko liczba, tak naprawdę nie potrzebujesz żadnej wartości właściwości i na pewno, podobnie jak w jednym z przykładów kodu powyżej, nie potrzebujesz również podelementów.

Tak więc kod powinien wyglądać następująco:

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

Mam nadzieję, że to pomoże.

Oscar Salguero
źródło
10

Uważam, że najłatwiejszym i najbardziej wydajnym sposobem liczenia obiektów jest ustawienie NSFetchRequesttypu wyniku na NSCountResultTypei wykonanie go NSManagedObjectContext countForFetchRequest:error:metodą.

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
źródło
6

Napisałem prostą metodę narzędziową dla Swift 3 do pobierania liczby obiektów.

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
źródło
3

W 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
źródło
1

To naprawdę tylko to:

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

„Łódź” to po prostu nazwa jednostki z ekranu Twojego modelu danych:

wprowadź opis obrazu tutaj

Co to jest globalne yourContainer?

Aby skorzystać z podstawowych danych, w pewnym momencie aplikacji, tylko raz, po prostu idziesz

var yourContainer = NSPersistentContainer(name: "stuff")

gdzie „rzeczy” to po prostu nazwa pliku modelu danych.

wprowadź opis obrazu tutaj

Po prostu miałbyś do tego singletona,

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

Czyli z dowolnego miejsca w aplikacji

core.container

jest twoim pojemnikiem,

Więc w praktyce, aby policzyć dowolną jednostkę, wystarczy

let k = try? core.container.viewContext.count(for: NSFetchRequest(entityName: "Boat"))
Fattie
źródło
0

Jeśli chcesz znaleźć liczbę dla określonego predykowanego pobierania, uważam, że jest to najlepszy sposób:

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

if(count > 0) {
NSLog(@"EXIST"); 
} else {
NSLog(@"NOT exist");
}
Umit Kaya
źródło