Używam RestKit do pobierania obiektów z mojej usługi RoR i używam CoreData do utrwalania niektórych obiektów (więcej obiektów tabeli wyszukiwania typu statycznego). TasteTag jest jednym z tych utrwalonych obiektów:

#ifdef RESTKIT_GENERATE_SEED_DB
    NSString *seedDatabaseName = nil;
    NSString *databaseName = RKDefaultSeedDatabaseFileName;
#else
    NSString *seedDatabaseName = RKDefaultSeedDatabaseFileName;
    NSString *databaseName = @"Model.sqlite";
#endif

RKObjectManager* manager = [RKObjectManager objectManagerWithBaseURL:kServerURL];  
manager.objectStore = [RKManagedObjectStore objectStoreWithStoreFilename:databaseName usingSeedDatabaseName:seedDatabaseName managedObjectModel:nil delegate:self];

.. lots of fun object mapping ..

 RKManagedObjectMapping* tasteTagMapping = [RKManagedObjectMapping mappingForClass:[TasteTag class]];
[tasteTagMapping mapKeyPath:@"id" toAttribute:@"tasteTagID"];
[tasteTagMapping mapKeyPath:@"name" toAttribute:@"name"];
tasteTagMapping.primaryKeyAttribute = @"tasteTagID";
[[RKObjectManager sharedManager].mappingProvider setMapping:tasteTagMapping forKeyPath:@"taste_tags"]; 
[[RKObjectManager sharedManager].mappingProvider addObjectMapping:tasteTagMapping];

.. some more mapping ..

Mam dane powracające z serwera RoR i są one mapowane na obiekty zgodnie z oczekiwaniami. Jednostka danych podstawowych również wydaje się dobrze zmapowana po tym, jak RestKit otrzyma żądanie z powrotem:

"<TasteTag: 0x6e87170> (entity: TasteTag; id: 0x6e85d60 <x-coredata://03E4A20A-21F2-4A2D-92B4-C4424893D559/TasteTag/p5> ; data: <fault>)"

Problem polega na tym, że gdy próbuję uzyskać dostęp do właściwości obiektów, usterka nie wydaje się być ogniem. Na początku tylko wywoływałem właściwości, które zawsze wracały jako zero (chociaż powinno to odpalić usterkę):

for (TasteTag *tag in self.vintage.tasteTags) {
    [tagNames addObject:tag.name]; //get error of trying to add nil to array   
}

Po zapoznaniu się z błędami wyzwalania ręcznego (http://www.mlsite.net/blog/?p=518 ) Próbowałem zadzwonić [tag willAccessValueForKey:nil], co skutkuje:

Terminating app due to uncaught exception 'NSObjectInaccessibleException', reason: 'CoreData could not fulfill a fault for '0x6e7b060 <x-coredata://03E4A20A-21F2-4A2D-92B4-C4424893D559/TasteTag/p5>''

Wyszukiwanie encji w .sqlite na podstawie klucza (TasteTag/p5) pokazuje, że jest ona zmapowana na oczekiwaną przeze mnie.

Inne posty dotyczące RestKit zalecają wyłączenie pamięci podręcznej obiektów (której nie używam), ponieważ jest to zwykle spowodowane usunięciem encji. Ale na tym etapie tylko czytam, nie usuwam i nie mam pamięci podręcznej.

Jeśli po prostu zadzwonię [TasteTag allObjects], jestem w stanie odzyskać wszystkie obiekty w porządku i ładują się bez problemu. Wydaje się, że tak jest tylko w przypadku, gdy są winni.

6
Parrots 12 styczeń 2012, 20:11
Mam nadzieję, że znajdziesz rozwiązanie, mam prawie identyczny problem.
 – 
Ryan Wersal
13 styczeń 2012, 11:14
Jeśli coś znajdziesz daj mi znać, do tej pory nie miałem szczęścia
 – 
Parrots
13 styczeń 2012, 23:54
- wygląda na to, że może nie być specyficzny dla RestKit. Pytanie uzupełniające tutaj: stackoverflow.com/questions/8856867/…
 – 
Parrots
14 styczeń 2012, 00:38
Znalazłem rozwiązanie, które zadziałało dla mnie. Starałem się być wyraźny w mojej odpowiedzi poniżej. Mam nadzieję, że to pomoże!
 – 
Ryan Wersal
16 styczeń 2012, 19:39

2 odpowiedzi

Najlepsza odpowiedź

Dokumentowanie mojej poprawki (czytaj: hack) zgodnie z sugestią Ryana.

Wydaje się, że błąd dotyczy tego, w jaki sposób RestKit założył, że będziesz używać obiektów zwróconych z ich metody objectLoader:didLoadObjects:. Wydaje się, że zakładają, że wszystko będzie oparte na danych podstawowych (i postępuj zgodnie z przepływem podobnym do tego, o czym mówił Ryan – pozwól mu zsynchronizować się z danymi podstawowymi, a następnie ponownie wykonaj zapytanie) lub że będziesz używać wszystkich obiektów nieobsługiwanych przez dane podstawowe i po prostu zachowaj te wyniki.

W moim przypadku miałem mieszankę - główną tablicę obiektów, które nie są wspierane przez Core Data, z których każdy zawierał tablicę jednostek opartych na Core Data. Obiekty najwyższego poziomu to te, o które nie mam nic przeciwko wysyłaniu zapytań do serwera i nie mam powodu, aby utrzymywać się lokalnie poza widokiem, w którym są pokazane. Wygląda na to, że po zakończeniu objectLoader:didLoadObjects: kontekstu obiektu zarządzanego wspierającego dane podstawowe jednostki w obrębie parametru objects są usuwane (przy założeniu, że będziesz o nie ponownie pytać), powodując, że wszelkie przyszłe wywołania tych jednostek będą traktowane jako błędy, nawet jeśli nie możesz wywołać błąd i załaduj dane (wyniki w NSObjectInaccessibleException).

Obszedłem to z brzydkim hackiem - w obrębie objectLoader:didLoadObjects: mam dostęp do jednego z kontekstów obiektów zarządzanych encji Core Data i kopiuję go do właściwości w widoku (self.context = [tag managedObjectContext];). Zapobiega to udostępnieniu kontekstu po zakończeniu objectLoader:didLoadObjects:, umożliwiając mi dostęp do encji bez problemów w późniejszym widoku.

Innym rozwiązaniem byłoby ręczne ponowne wysłanie zapytania dla każdej jednostki przy użyciu nowego kontekstu i skopiowanie go z powrotem do przechowywanych obiektów zwracanych. Można to zrobić, gdy idzie się je wyświetlić, lub ewentualnie przetworzyć w objectLoader:didLoadObjects:, używając nowego kontekstu. Identyfikator jednostki nadal znajduje się w uszkodzonym obiekcie, więc można go użyć do ponownego zapytania bez problemu, nawet po zniknięciu oryginalnego kontekstu RestKit. Ale wydaje się niemądre, aby w ten sposób ponownie pytać o każdą jednostkę na grafie obiektów.

4
Parrots 16 styczeń 2012, 20:41
Możesz wyłączyć automatyczną synchronizację i ręcznie przechowywać obiekty w modelu CD, kiedy chcesz. Ale potrzebuję trochę dokumentacji na ten temat! Dzięki i tak :)
 – 
Neru-J
16 lipiec 2012, 15:45

Znalazłem rozwiązanie, które zadziałało dla mnie (nie jestem pewien, jak będzie pasowało do Twojej sytuacji, ale dodaję je jako odpowiedź, ponieważ rozwiązało to dla mnie ten (lub bardzo podobny) problem):

Kilka dni temu uruchomiłem przykład RKTwitterCoreData i zauważyłem, że działał idealnie, podczas gdy mój, z bardzo prostym kodem w tym momencie i robiący prawie to samo, nie działał. Mam dużo niespełnionych błędów. Postanowiłem więc zmodyfikować cały mój kod dotyczący RestKit, aby odzwierciedlić, jak robi to przykład RKTwitterCoreData.

Podzielę to na kawałki, aby pomóc ci podążać za moim tokiem myślenia w tym czasie (ponieważ nie sądzę, że nasze problemy są identyczne).

Moje pierwotne założenie implementacji

Ponieważ RestKit może przywrócić obiekty do danych podstawowych, założyłem, że te zarządzane obiekty mogą być używane zamiennie. Na przykład mógłbym użyć obiektów z Core Data w dokładnie taki sam sposób, jak te pobrane ze zdalnej usługi sieciowej. Mógłbym nawet połączyć je ze sobą, aby uzyskać wszystkie dane.

Myliłem się

Zauważyłem, że kod RKTwitterCoreData nie w najmniejszym stopniu nie działał w ten sposób. Przyzwoity kawałek mojego kodu pasował do ich kodu, ale największą różnicą było to, że nie traktowali tych obiektów jako wymiennych. W rzeczywistości nigdy nie używali obiektów, które otrzymali ze zdalnych magazynów danych. Zamiast tego po prostu pozwolili, aby „spadło przez szczeliny”. Mogę tylko założyć, że oznacza to, że zostały dodane do magazynu danych Core Data, ponieważ działa to dla nich, a teraz dla mnie.

Szczegóły

Moja aplikacja działała po zmodyfikowaniu kodu w celu wykorzystania tego przepływu. Mogę tylko wtedy przypuszczać, że niewykonalne błędy, które widzimy, są związane z używaniem obiektów opartych na danych podstawowych, które otrzymujemy z usługi sieciowej. Jeśli zamiast tego po prostu je zignorujesz, a następnie wykonasz pobieranie, otrzymasz wszystko z powrotem (w tym ostatnią prośbę) i nie powinieneś otrzymać żadnych niemożliwych do spełnienia błędów.

Aby rozwinąć, jeśli spojrzysz na RKTwitterViewController, zauważysz, że wiersze 45-61 obsługują ładowanie obiektów:

- (void)loadObjectsFromDataStore {
    [_statuses release];
    NSFetchRequest* request = [RKTStatus fetchRequest];
    NSSortDescriptor* descriptor = [NSSortDescriptor sortDescriptorWithKey:@"createdAt" ascending:NO];
    [request setSortDescriptors:[NSArray arrayWithObject:descriptor]];
    _statuses = [[RKTStatus objectsWithFetchRequest:request] retain];
}

- (void)loadData {
    // Load the object model via RestKit    
    RKObjectManager* objectManager = [RKObjectManager sharedManager];
    [objectManager loadObjectsAtResourcePath:@"/status/user_timeline/RestKit" delegate:self block:^(RKObjectLoader* loader) {
        // Twitter returns statuses as a naked array in JSON, so we instruct the loader
        // to user the appropriate object mapping
        loader.objectMapping = [objectManager.mappingProvider objectMappingForClass:[RKTStatus class]];
    }];
}

Wszystko wygląda normalnie (przynajmniej w porównaniu do tego, jak początkowo robiłem to ładowanie). Ale spójrz na metodę delegata objectLoader:didLoadObjects::

- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects {
    [[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:@"LastUpdatedAt"];
    [[NSUserDefaults standardUserDefaults] synchronize];
    NSLog(@"Loaded statuses: %@", objects);
    [self loadObjectsFromDataStore];
    [_tableView reloadData];
}

Próbka nawet nie dotyka parametru objects! (Oczywiście oprócz NSLog...)

Wniosek/tl;dr

Nie używaj zarządzanych obiektów, które otrzymujesz z powrotem w objectLoader:didLoadObjects:, tak jakby były w pełni wspierane przez dane podstawowe. Zamiast tego zignoruj ​​je i ponownie pobierz z danych podstawowych. Znajdują się tam wszystkie obiekty, w tym te z ostatniego żądania. W przeciwnym razie dostaniesz wady nie do spełnienia (przynajmniej ja to zrobiłem).

10
Ryan Wersal 16 styczeń 2012, 19:38
Doszedłem do tego samego wniosku podczas debugowania. Wydaje się, że zakładają, że pozwolisz na synchronizację z CD, a następnie ponowne zapytanie. Problem, jaki mam z tym, polega na tym, że nie planowałem mieć wszystkich moich obiektów na dysku CD (nie mam ochoty przechowywać ich na później - tylko ten jeden widok renderowania). To zmusza mnie do korzystania ze zwróconych przedmiotów. Skończyło się na naprawieniu (czytaj: hakowanie w brzydki sposób), zachowując managedContext NSManagedObject w viewController. Dzięki temu mogę uzyskać dostęp do obiektów w porządku cellForIndexPath.
 – 
Parrots
16 styczeń 2012, 19:52
Może warto poświęcić swój czas, aby dodać to jako odpowiedź dla innych nieszczęśliwych podróżników tego aspektu RestKit. Jestem pod wrażeniem biblioteki, ale wydaje mi się dziwne, że to zastrzeżenie nie jest lepiej udokumentowane.
 – 
Ryan Wersal
16 styczeń 2012, 20:06
Uzgodnij dokumentację. Naprawdę przydałoby mi się kilka aktualnych dokumentów zamiast starszych. Dzięki za wgląd tutaj.
 – 
Neru-J
16 lipiec 2012, 15:43