Jak radzić sobie z tymczasowymi instancjami NSManagedObject?

Muszę utworzyć NSManagedObject instancje, zrobić z nimi kilka rzeczy, a następnie je zniszczyć lub zapisać do SQLite db. Problem polega na tym, że nie mogę tworzyć instancji NSManagedObject niepowiązanych z NSManagedObjectContext, a to oznacza, że muszę się jakoś oczyścić po tym, jak zdecyduję, że nie potrzebuję niektórych obiektów w moim db.

Aby sobie z tym poradzić, stworzyłem magazyn w pamięci przy użyciu tego samego koordynatora i umieszczam tam tymczasowe obiekty za pomocą assignObject:toPersistentStore. Teraz, Jak mam się upewnić, że te tymczasowe obiekty nie dostaną się do dane, które pobieram ze wspólnego dla obu sklepów kontekstu? Czy muszę tworzyć osobne konteksty dla takiego zadania?


Upd:

Teraz myślę o stworzeniu osobnego kontekstu dla magazynu w pamięci. Jak przenieść obiekty z jednego kontekstu do drugiego? Po prostu za pomocą [context insertObject:]? Czy to działa OK w tej konfiguracji? Jeśli wstawiam jeden obiekt z wykresu obiektów, to czy cały wykres również zostanie wstawiony do kontekstu?
Author: Andriy, 2010-07-15

8 answers

UWAGA: ta odpowiedź jest bardzo stara. Zobacz komentarze do pełnej historii. Od tego czasu moje rekomendacje uległy zmianie i nie zalecam już używania niezwiązanych instancji NSManagedObject. Obecnie zalecam używanie tymczasowych instancji child NSManagedObjectContext.

Oryginalna Odpowiedź

Najprostszym sposobem na to jest utworzenie instancji NSManagedObject bez powiązanego NSManagedObjectContext.

NSEntityDescription *entity = [NSEntityDescription entityForName:@"MyEntity" inManagedObjectContext:myMOC];
NSManagedObject *unassociatedObject = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];

Wtedy gdy chcesz go zapisać:

[myMOC insertObject:unassociatedObject];
NSError *error = nil;
if (![myMoc save:&error]) {
  //Respond to the error
}
 143
Author: Marcus S. Zarra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-20 18:16:23

IOS5 stanowi prostszą alternatywę dla odpowiedzi Mike ' a Wellera. Zamiast tego użyj potomka NSManagedObjectContext. Usuwa potrzebę trampoliny przez NSNotificationCenter

Aby utworzyć kontekst potomny:

NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
childContext.parentContext = myMangedObjectContext;

Następnie utwórz obiekty używając kontekstu potomnego:

NSManagedObject *o = [NSEntityDescription insertNewObjectForEntityForName:@"MyObject" inManagedObjectContext:childContext];

Zmiany są stosowane tylko wtedy, gdy kontekst potomny jest zapisywany. Tak więc, aby odrzucić zmiany, po prostu nie zapisuj.

Nadal istnieje ograniczenie związków. ie nie można utworzyć relacje z obiektami w innych kontekstach. Aby obejść to użyj objectID ' s, aby uzyskać obiekt z kontekstu potomnego. np.

NSManagedObjectID *mid = [myManagedObject objectID];
MyManagedObject *mySafeManagedObject = [childContext objectWithID:mid];
object.relationship=mySafeManagedObject;

Uwaga, zapisanie kontekstu potomnego stosuje zmiany do kontekstu rodzica. Zapisywanie kontekstu nadrzędnego trwa nadal.

Zobacz wwdc 2012 sesja 214 aby uzyskać pełne wyjaśnienie.

 37
Author: railwayparade,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-07 22:19:06

Poprawnym sposobem osiągnięcia tego rodzaju rzeczy jest użycie nowego kontekstu zarządzanego obiektu. Tworzy się kontekst zarządzanego obiektu z tym samym trwałym magazynem:

NSManagedObjectContext *tempContext = [[[NSManagedObjectContext alloc] init] autorelease];
[tempContext setPersistentStore:[originalContext persistentStore]];

Następnie dodajesz nowe obiekty, mutujesz je itp.

Kiedy przychodzi czas na zapisanie, musisz wywołać [tempContext save:...] na tempContext i obsłuż powiadomienie Zapisz, aby połączyć je z oryginalnym kontekstem. Aby odrzucić obiekty, po prostu zwolnij ten tymczasowy kontekst i zapomnij o nim.

Więc kiedy zapisujesz tymczasowy kontekst, zmiany są utrzymywane w sklepie i musisz tylko wprowadzić te zmiany z powrotem do głównego kontekstu:

/* Called when the temp context is saved */
- (void)tempContextSaved:(NSNotification *)notification {
    /* Merge the changes into the original managed object context */
    [originalContext mergeChangesFromContextDidSaveNotification:notification];
}

// Here's where we do the save itself

// Add the notification handler
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(tempContextSaved:)
                                             name:NSManagedObjectContextDidSaveNotification
                                           object:tempContext];

// Save
[tempContext save:NULL];
// Remove the handler again
[[NSNotificationCenter defaultCenter] removeObserver:self
                                                name:NSManagedObjectContextDidSaveNotification
                                              object:tempContext];

Jest to również sposób, w jaki powinieneś obsługiwać wielowątkowe operacje na danych. Jeden kontekst na wątek.

Jeśli chcesz uzyskać dostęp do istniejących obiektów z tego tymczasowego kontekstu (aby dodać relacje itp.) następnie musisz użyć ID obiektu, aby uzyskać nową instancję w ten sposób:

NSManagedObject *objectInOriginalContext = ...;
NSManagedObject *objectInTemporaryContext = [tempContext objectWithID:[objectInOriginalContext objectID]];

Jeśli spróbujesz użyć NSManagedObject w niewłaściwym kontekście podczas zapisywania otrzymasz wyjątki.

 9
Author: Mike Weller,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-15 14:37:32

Tworzenie obiektów tymczasowych z kontekstu zerowego działa dobrze, dopóki nie spróbujesz nawiązać relacji z obiektem, którego kontekst != zero!

Upewnij się, że zgadzasz się z tym.

 9
Author: user134611,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-03 12:55:32

To, co opisujesz, jest dokładnie tym, do czego służy NSManagedObjectContext.

From Core Data Programming Guide: Core Data Basics

Zarządzany kontekst obiektu można traktować jako inteligentną podkładkę pod zarysowania. Pobieranie obiektów z magazynu trwałego powoduje przeniesienie tymczasowych kopii na obszar magazynowania, w którym tworzą one Wykres obiektu (lub zbiór Wykresów obiektowych). Następnie możesz modyfikować te obiekty w dowolny sposób. Chyba, że faktycznie zapiszesz te zmiany, jednak trwały magazyn pozostaje niezmieniony.

I Przewodnik Programowania Podstawowych Danych: Zarządzana Walidacja Obiektów

Jest to również podstawą idei zarządzanego kontekstu obiektu reprezentującego "scratch pad" -ogólnie można przenieść zarządzane obiekty na scratch pad i edytować je w dowolny sposób, zanim ostatecznie zatwierdzisz zmiany lub je odrzucisz.

NSManagedObjectContexts są zaprojektowane tak, aby były lekkie. Można je tworzyć i odrzucać do woli - jest to uporczywe sklepy i to one są "ciężkie". Pojedynczy koordynator trwałego sklepu może mieć wiele kontekstów związanych z nim. W przypadku starszego, przestarzałego modelu ograniczania wątków oznaczałoby to ustawienie tego samego koordynatora trwałego sklepu w każdym kontekście. Obecnie oznaczałoby to połączenie zagnieżdżonych kontekstów z kontekstem głównym, który jest powiązany z koordynatorem trwałego magazynu.

Tworzenie kontekstu, tworzenie i modyfikowanie zarządzanych obiektów w tym kontekście. Jeśli chcesz je utrzymywać i komunikować te zmiany, Zapisz kontekst. W przeciwnym razie należy go wyrzucić.

Próba utworzenia zarządzanych obiektów niezależnie od NSManagedObjectContext wymaga kłopotów. Pamiętaj, że podstawowe dane są ostatecznie mechanizmem śledzenia zmian dla wykresu obiektowego. Z tego powodu zarządzane obiekty są w rzeczywistości częścią kontekstu zarządzanych obiektów . Kontekst obserwuje ich cykl życia i bez kontekstu nie wszystkie funkcje zarządzanych obiektów będą działaj poprawnie.

 8
Author: quellish,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-15 10:44:04

W zależności od zastosowania obiektu tymczasowego istnieją pewne zastrzeżenia do powyższych zaleceń. Mój przypadek użycia polega na tym, że chcę utworzyć obiekt tymczasowy i powiązać go z widokami. Gdy użytkownik zdecyduje się zapisać ten obiekt, chcę ustawić relacje z istniejącymi obiektami i zapisać. Chcę to zrobić, aby uniknąć tworzenia tymczasowego obiektu do przechowywania tych wartości. (Tak, mogę po prostu poczekać, aż użytkownik zapisze, a następnie pobrać zawartość widoku, ale umieszczam te widoki wewnątrz tabeli i logika jest mniej elegancka.)

Opcje dla obiektów tymczasowych to:

1) (preferowane) tworzy obiekt tymczasowy w kontekście potomnym. To nie zadziała, ponieważ wiążę obiekt z interfejsem użytkownika i nie mogę zagwarantować, że Accesory do obiektów są wywoływane w kontekście potomnym. (Nie znalazłem żadnej dokumentacji, która stanowi inaczej, więc muszę założyć.)

2) Utwórz obiekt tymczasowy z zerowym kontekstem obiektu. To nie działa i skutkuje danymi strata/korupcja.

Moje Rozwiązanie: Rozwiązałem to tworząc tymczasowy obiekt z zerowym kontekstem obiektu, ale kiedy zapisuję obiekt, zamiast wstawiać go jako #2, kopiuję wszystkie jego atrybuty do nowego obiektu, który tworzę w głównym kontekście. Stworzyłem w mojej podklasie nsmanagedobject metodę wspierającą cloneInto: która pozwala mi łatwo kopiować atrybuty i relacje dla dowolnego obiektu.

 6
Author: greg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-09 19:08:41

Dla mnie odpowiedź Marcusa nie zadziałała. Oto co mi się udało:

NSEntityDescription entityForName:@"MyEntity" inManagedObjectContext:myMOC];
NSManagedObject *unassociatedObject = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];

Wtedy, jeśli zdecyduję się go zapisać:

[myMOC insertObject:unassociatedObjet];
NSError *error = nil;
[myMoc save:&error];
//Check the error!

Nie możemy też zapomnieć o jej wydaniu

[unassociatedObject release]
 1
Author: Lucas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-28 09:49:09

Przepisuję tę odpowiedź dla Swifta, tak jak wszystkie podobne pytania dla Swifta Przekierowuję na to pytanie.

Możesz zadeklarować obiekt bez ManagedContext używając poniższego kodu.

let entity = NSEntityDescription.entity(forEntityName: "EntityName", in: myContext)
let unassociatedObject = NSManagedObject.init(entity: entity!, insertInto: nil)

Później, aby zapisać obiekt, możesz wstawić go do kontekstu i zapisać.

myContext.insert(unassociatedObject)
// Saving the object
do {
    try self.stack.saveContext()
    } catch {
        print("save unsuccessful")
    }
}
 0
Author: Mitul Jindal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-17 10:49:52