2009-09-03 11 views
18

Jest to aplikacja na iPhone'a, ale nie sądzę, że to naprawdę ma znaczenie. Muszę wysłać niestandardowy obiekt (który jest zarządzany przez Core Data) przez bluetooth za pomocą GameKita iPhone'a. Normalnie użyłbym NSKeyedArchiver do spakowania obiektu jako obiektu danych i wysłania go przez linię, a następnie cofnięcia archiwizacji obiektu i skończyłem. Oczywiście, musiałbym również zaimplementować metody initWithCoder: i encodeWithCoder: w moim niestandardowym obiekcie.Czy mogę zakodować podklasę obiektu NSManagedObject?

Nie jestem pewien, czy można to zrobić z klasą NSManagedObject zarządzaną przez Core Data, czy nie. Czy będą razem fajnie grać? Zgaduję, że po wysłaniu zakodowanego zarządzanego obiektu do drugiego urządzenia i jego odkodowaniu, po prostu dodałbym ten otrzymany obiekt do kontekstu innego urządzenia. Czy to jest poprawne? Czy brakuje mi jakichkolwiek kroków?

Odpowiedz

27

Instancja NSManagedObject nie można sensownie istnieć poza instancji NSManagedObjectContext, więc nie próbuj robić NSCoding tańce wymaganych bezpośrednio serializacji i deserializacji e NSManagedObject między dwóch kontekstach (można to zrobić, patrz poniżej) . Zamiast tego utworzyłbym słownik z odpowiednimi kluczami/wartościami atrybutu (możesz uzyskać nazwy atrybutów za pośrednictwem nazw atrybutów instancji zarządzanego obiektu przez instance.entity.attributesByName.allKeys (możesz użyć [instance dictionaryWithValuesForKeys:keys], aby uzyskać słownik par atrybut: wartość). jako NSURL-kodowany NSManagedObjectIDs. Nie zapomnij dołączyć do słownika instancji managedObjectID (jako NSURL) w słowniku, aby móc ponownie połączyć dowolne relacje z obiektem na drugim końcu. Będziesz musiał rekurencyjnie utworzyć te słowniki dla dowolne cele relacji dla instancji, którą kodujesz.

Następnie wyślij dyktando (y) przez przewód i rekonstruuj je na drugim końcu jako wystąpienia w nowym zarządzanym kontekst obiektu (można użyć setValuesForKeysWithDictionary:).

Można zauważyć, że to właśnie System NSCoder zrobiłby dla ciebie, chyba trzeba by użyć classForCoder, replacementObjectForCoder: i awakeAfterUsingCoder: wraz z niestandardowych NSDictionary podklasy do obsługi wszystkich mapowanie NSManageObject -to- NSDictionary i wizy versa. Ten kod jest bardziej kłopotliwy, niż jest to warte, z mojego doświadczenia, chyba że masz złożony/głęboki wykres obiektów, które próbujesz serializować. Dla pojedynczej instancji NSManagedObject bez relacji, zdecydowanie łatwiej jest po prostu przeprowadzić konwersję na dyktando i cofnąć się.

+0

Miałem ten sam problem i skończyło się na wykonaniu dokładnie tego, co sugerował Barry - wszystko działało dobrze. – jeff7091

+0

byłoby dobrze również zamiast NSDictionary, po prostu będę miał identyczny model z moim NSManagedObject? i tam zrobię mapowanie? – bluezald

+0

Czy ktokolwiek mógłby wyjaśnić, jak NSManagedObjectID zakodowane NSURL jest pomocne w relacjach? Dzięki! –

2

Proponuję rozwiązanie słownikowe dla prostszych opcji. Jednak oto w jaki sposób rozwiązałem problem. Mój model był już spory i solidny, z niestandardowymi klasami i jedną klasą główną powyżej NSManagedObject.

Wszystko, co potrzebne było do tego pojedynczy klasa wywołać odpowiedni wyznaczony initializer z NSManagedObject: [super initWithEntity:insertIntoManagedObjectContext:]. Ta metoda i metadane w NSEntityDescription są tym, co konfiguruje implementacje wszystkich dynamicznych akcesorów.

- (id)initWithCoder:(NSCoder *)aDecoder { 
    CoreDataStack *cds = [LibraryDiscoverer unarchivingCoreDataStack]; 
    NSEntityDescription *entity = [cds entityDescriptionForName:[[self class] entityName]]; 
    NSManagedObjectContext *moc = [cds managedObjectContext]; 
    self = [super initWithEntity:entity insertIntoManagedObjectContext:moc]; 
    self.lastEditDate = [aDecoder decodeObjectForKey:@"lastEditDate"]; 
    return self; 
} 

The CoreDataStack to moja abstrakcja wokół CoreData. LibraryDiscoverer to globalny hak dostępu, aby uzyskać podstawowe informacje o danych. entityName to metoda zdefiniowana w celu podania nazwy jednostki z nazwy klasy; jeśli zastosujesz konwencję nazewnictwa (to jest nazwa klasy = nazwa jednostki), może ona zostać zaimplementowana w sposób ogólny.

Wszystkie inne metody initWithCoder: w mojej klasie są standardowe NSCoder, z tą różnicą, że nie trzeba kodować obu kierunków relacji, CoreData ponownie je dla Ciebie. (Jak zawsze, łącznie z rozwiązaniem słownika).

+0

Ładne rozwiązanie. Jak sobie z tym poradzisz, kiedy masz dwa stosy? Obecnie pracuję nad aplikacją, która komunikuje się z inną instancją aplikacji za pośrednictwem sieci. W moim celu testowym mam test jednostkowy, który testuje komunikację. W tej sytuacji potrzebuję dwóch różnych stosów. Jak rozwiązałbyś to w połączeniu z powyższym przykładem? –

+0

W rzeczywistości tworzę nowy, unikalny stos, aby wykonać archiwizację, następnie zapisuję kontekst (główny stos nasłuchuje i aktualizuje), a następnie usuwam stos importujący. - - - * możesz * zachować mapowanie NSCoder -> stos, jeśli chcesz uzyskać złożony dekoder "unarchivingCoreDataStataForCoder: (NSCoder *)" – bshirley

Powiązane problemy