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

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

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;

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).


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.


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

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
}

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
}

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

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");
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.