iPhone Core Data Nierozwiązany błąd podczas zapisywania

169

Otrzymuję dziwny komunikat o błędzie z podstawowych danych podczas próby zapisania, ale problem polega na tym, że błąd nie jest odtwarzalny (pojawia się w różnym czasie podczas wykonywania różnych zadań)

komunikat o błędzie:

Unresolved error Domain=NSCocoaErrorDomain Code=1560 UserInfo=0x14f5480 "Operation could not be completed. (Cocoa error 1560.)", {
NSDetailedErrors = (
Error Domain=NSCocoaErrorDomain Code=1570 UserInfo=0x5406d70 "Operation could not be completed. (Cocoa error 1570.)",
Error Domain=NSCocoaErrorDomain Code=1570 UserInfo=0x14f9be0 "Operation could not be completed. (Cocoa error 1570.)"
);
}

a metoda generująca błąd to:

- (IBAction)saveAction:(id)sender {
    NSError *error;
    if (![[self managedObjectContext] save:&error]) {
        // Handle error
        NSLog(@"Unresolved error %@, %@, %@", error, [error userInfo],[error localizedDescription]);
        exit(-1);  // Fail
    }
}

jakiś pomysł na powód tej wiadomości? dając, że pojawia się losowo

Ahmed Kotb
źródło
To może ci pomóc: „Obsługa błędów danych podstawowych iPhone'a„ Produkcja ”” stackoverflow.com/questions/2262704/ ...
Johannes Fahrenkrug

Odpowiedzi:

296

Oznacza to, że przypisano zerową właściwość obowiązkową. Albo w swoim * .xcodatamodelu zaznacz pole „opcjonalne” lub podczas zapisywania w managedObjectContext upewnij się, że Twoje właściwości są wypełnione.

Jeśli po zmianie kodu w celu dopasowania do dwóch wymagań pojawiają się kolejne błędy, spróbuj wyczyścić kompilację i usunąć aplikację z urządzenia iPhone Simulator / iPhone. Twoja zmiana modelu może kolidować ze starą implementacją modelu.

Edytować:

Prawie zapomniałem o wszystkich kodach błędów, które wypluwa Core Data: Odniesienie do stałych danych podstawowych : Miałem z tym wcześniej problem i zdałem sobie sprawę, że odznaczyłem poprawne opcjonalne pole. Taki problem ze znalezieniem problemu. Powodzenia.

David Wong
źródło
2
To rozwiązało to dla mnie. Zauważ również, że przynajmniej z mojego doświadczenia, mimo że nie został zapisany w pliku sqlite, zmiany dotarły do ​​kontekstu. W takim przypadku zachowanie może być nieobliczalne.
nickthedude
Nie udało mi się dotrzeć do głównej przyczyny, ale udało mi się rozwiązać problem, ustawiając wszystkie właściwości jako opcjonalne.
Michael Osofsky
Czy wypróbowałeś kod Charlesa, powiedziałby ci, w którym polu występuje problem.
David Wong,
233

Sam przez chwilę z tym walczyłem. Prawdziwy problem polega na tym, że debugowanie, które masz, nie pokazuje, na czym polega problem. Powodem tego jest to, że CoreData umieści tablicę obiektów NSError w obiekcie NSError "najwyższego poziomu", który zwraca, jeśli jest więcej niż jeden problem (dlatego pojawia się błąd 1560, który wskazuje wiele problemów i tablicę błędów 1570). Wygląda na to, że CoreData ma kilka kluczy, których używa do przechowywania informacji w zwracanym błędzie, jeśli wystąpi problem, który da ci bardziej przydatne informacje (takie jak encja, w której wystąpił błąd, związek / atrybut, którego brakowało itp. ). Klucze używane do sprawdzania słownika informacji o użytkowniku można znaleźć w dokumentacji referencyjnej tutaj .

Oto blok kodu, którego używam, aby uzyskać rozsądne dane wyjściowe z błędu zwróconego podczas zapisywania:

    NSError* error;
    if(![[survey managedObjectContext] save:&error]) {
        NSLog(@"Failed to save to data store: %@", [error localizedDescription]);
        NSArray* detailedErrors = [[error userInfo] objectForKey:NSDetailedErrorsKey];
        if(detailedErrors != nil && [detailedErrors count] > 0) {
            for(NSError* detailedError in detailedErrors) {
                NSLog(@"  DetailedError: %@", [detailedError userInfo]);
            }
        }
        else {
            NSLog(@"  %@", [error userInfo]);
        }
    }

Spowoduje to wyświetlenie danych wyjściowych z informacją o brakujących polach, co znacznie ułatwia rozwiązanie problemu.

Charles
źródło
Wielkie dzięki za ten kod. To sprawia, że ​​śledzenie problemów w CoreData jest rzeczywiście znacznie prostsze.
MiKL
21

Wrzucam to jako odpowiedź, chociaż jest to bardziej upiększenie fragmentu Charlesa. Proste wyjście z NSLog może być bałaganem do odczytania i zinterpretowania, więc lubię dorzucić trochę białych znaków i wywołać wartość niektórych krytycznych kluczy „userInfo”.

Oto wersja metody, której używałem. („_sharedManagedObjectContext” to #define dla „[[[UIApplication sharedApplication] delegate] managedObjectContext]”.)

- (BOOL)saveData {
    NSError *error;
    if (![_sharedManagedObjectContext save:&error]) {
        // If Cocoa generated the error...
        if ([[error domain] isEqualToString:@"NSCocoaErrorDomain"]) {
            // ...check whether there's an NSDetailedErrors array            
            NSDictionary *userInfo = [error userInfo];
            if ([userInfo valueForKey:@"NSDetailedErrors"] != nil) {
                // ...and loop through the array, if so.
                NSArray *errors = [userInfo valueForKey:@"NSDetailedErrors"];
                for (NSError *anError in errors) {

                    NSDictionary *subUserInfo = [anError userInfo];
                    subUserInfo = [anError userInfo];
                    // Granted, this indents the NSValidation keys rather a lot
                    // ...but it's a small loss to keep the code more readable.
                    NSLog(@"Core Data Save Error\n\n \
                      NSValidationErrorKey\n%@\n\n \
                      NSValidationErrorPredicate\n%@\n\n \
                      NSValidationErrorObject\n%@\n\n \
                      NSLocalizedDescription\n%@", 
                      [subUserInfo valueForKey:@"NSValidationErrorKey"], 
                      [subUserInfo valueForKey:@"NSValidationErrorPredicate"], 
                      [subUserInfo valueForKey:@"NSValidationErrorObject"], 
                      [subUserInfo valueForKey:@"NSLocalizedDescription"]);
                }
            }
            // If there was no NSDetailedErrors array, print values directly
            // from the top-level userInfo object. (Hint: all of these keys
            // will have null values when you've got multiple errors sitting
            // behind the NSDetailedErrors key.
            else {
                    NSLog(@"Core Data Save Error\n\n \
                      NSValidationErrorKey\n%@\n\n \
                      NSValidationErrorPredicate\n%@\n\n \
                      NSValidationErrorObject\n%@\n\n \
                      NSLocalizedDescription\n%@", 
                      [userInfo valueForKey:@"NSValidationErrorKey"], 
                      [userInfo valueForKey:@"NSValidationErrorPredicate"], 
                      [userInfo valueForKey:@"NSValidationErrorObject"], 
                      [userInfo valueForKey:@"NSLocalizedDescription"]);

            }
        } 
        // Handle mine--or 3rd party-generated--errors
        else {
            NSLog(@"Custom Error: %@", [error localizedDescription]);
        }
        return NO;
    }
    return YES;
}

To pozwala mi zobaczyć wartość `` NSValidationErrorKey '', która, gdy napotkałem problem z OP, wskazywała bezpośrednio na nie opcjonalne jednostki Core Data, których zapomniałem ustawić przed próbą zapisania.

clozach
źródło
Również bardzo przydatne. Szczególnie, gdy otrzymujesz te surowe \ n \ n \ n ciągi opisujące podstawowe jednostki danych.
Łukasz
Schludny. Przy okazji „wiadomość” jest nieużywana.
pojo
0

Problem dotknął mnie, kiedy zapisuję drugi rekord w CoreData. Wszystkie pola nieobowiązkowe (relacja) zostały również wypełnione bez zera, ale w wyniku błędu zauważyłem, że jedno z pól w pierwszym zapisanym obiekcie stało się zerowe. Trochę dziwne? Ale powód jest dość banalny - relacja jeden do jednego, która unieważnia pierwszy obiekt, gdy ustawię go w drugim.

Tak więc schemat jest taki:

"Parent" with relationship "child" One to One
Create Child 1, set parent. Save - OK
Create Child 2, set parent. Save - Error, Child 1.Parent == nil
(behind the scene child 2 did nullify child 1 parent)

Zmiana relacji w Rodzinie z Jeden na Jeden na Wiele w Jeden rozwiązała to zadanie.

HotJard
źródło
0

Miałem przejściową właściwość typu int, która nie była opcjonalna. Oczywiście przy ustawieniu na 0 pojawia się błąd 1570. Właśnie zmieniłem wszystkie moje właściwości przejściowe na opcjonalne. W razie potrzeby można zaimplementować w kodzie logikę zerową.

Anton Plebanovich
źródło
0

Oznacza to, że Twój model nie został zweryfikowany, co może się zdarzyć z wielu powodów: nieużywana właściwość w modelu, brakująca wartość oznaczona jako wymagana. Aby lepiej zrozumieć, co poszło nie tak, ustaw punkt przerwania w miejscu, w którym jesteś gotowy do zapisania obiektu, i wywołaj jeden z validateFor...wariantów metody, na przykład:

po [myObject validateForInsert]

Bardziej szczegółowe informacje o problemie znajdują się w opisie błędu. Pomyślna weryfikacja oznacza, że ​​nie otrzymasz żadnych wyników.

kkodev
źródło
0

Pomogło mi. Sprawdź też to.

Zaznacz opcjonalne pole w obiektach * .xcodatamodel

ssowri1
źródło