Geçici NSManagedObject örnekleriyle nasıl başa çıkılır?


86

NSManagedObjectÖrnekler oluşturmam , onlarla bazı şeyler yapmam ve sonra bunları çöpe atmam veya sqlite db'ye kaydetmem gerekiyor. Sorun şu ki, NSManagedObjectbağlantısız örnekler oluşturamıyorum NSManagedObjectContextve bu , veritabanımdaki bazı nesnelere ihtiyacım olmadığına karar verdikten sonra bir şekilde temizlemem gerektiği anlamına geliyor.

Bununla başa çıkmak için, aynı koordinatörü kullanarak bir bellek içi depo oluşturdum ve assignObject:toPersistentStore.Now'ı kullanarak oraya geçici nesneler yerleştiriyorum , bu geçici nesnelerin verilere ulaşmamasını nasıl sağlayabilirim ki her iki mağaza bağlamında ortak mı? Yoksa böyle bir görev için ayrı bağlamlar mı oluşturmalıyım?


UPD:

Şimdi bellek içi depo için ayrı bağlam oluşturmayı düşünüyorum. Nesneleri bir bağlamdan diğerine nasıl taşıyabilirim? Yalnızca [context insertObject:] mi kullanıyorsunuz? Bu kurulumda iyi çalışacak mı? Nesnelerin grafiğinden bir nesne eklersem, tüm grafik de bağlama yerleştirilir mi?


Bu soruyu yanıtlanmış olarak işaretlediğiniz için bu ayrı bir soru olmalıdır. Yeni bir soru oluşturun ve açıklamak NEDEN size yığın ayrı tüm Çekirdek Veri ihtiyaç hissetmeyecek SADECE bir bellek içi mağaza için. Soruyu sizinle birlikte keşfetmekten mutlu olacağım.
Marcus S. Zarra

UPD bölümü artık alakalı değil, çünkü başka bir yaklaşım seçtim, cevabınıza son yorumuma bakın.
fspirit

Yanıtlar:


146

NOT: Bu cevap çok eskidir. Tam geçmiş için yorumlara bakın. O zamandan beri tavsiyem değişti ve artık ilişkilendirilmemiş NSManagedObjectörnekleri kullanmanızı önermiyorum . Şu anki önerim geçici alt NSManagedObjectContextörnekleri kullanmaktır .

Orijinal Cevap

Bunu yapmanın en kolay yolu, NSManagedObjectörneklerinizi ilişkilendirilmeden oluşturmaktır NSManagedObjectContext.

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

Sonra onu kaydetmek istediğinizde:

[myMOC insertObject:unassociatedObject];
NSError *error = nil;
if (![myMoc save:&error]) {
  //Respond to the error
}

6
UnassociatedObject diğer ilişkilendirilmemiş nesnelere refler içeriyorsa, bunları tek tek eklemeli miyim yoksa myMOC tüm referansları toplayıp bunları da ekleyecek kadar akıllı mı?
fspirit

6
İlişkileri de idare edecek kadar akıllı.
Marcus S. Zarra

2
Bu yaklaşımın, saklamaya karar vermeden önce MO'lara normal veri nesneleri gibi davranmanızı sağlaması hoşuma gidiyor, ancak CoreData sözleşmesi tarafından ne kadar "desteklendiğinden" ve dolayısıyla ne kadar geleceğe hazır olduğundan endişeleniyorum. Elma bu yaklaşımı herhangi bir yerde kullanıyor mu veya kullanıyor mu? Aksi takdirde, gelecekteki bir iOS sürümü dinamik özellikleri MOC'ye bağlı olacak şekilde değiştirebilir ve bu yaklaşımı bozabilir. Apple dokümanları bu konuda net değil: bağlamın ve atanan başlatıcının önemini vurguluyorlar, ancak MO dokümanında "eğer içerik sıfır değilse, o zaman ..." diyerek sıfırın uygun olabileceğini düşündüren bir söz var
Rhubarb

41
Bu yaklaşımı bir süre önce kullandım, ancak bu nesneleri değiştirdiğimde ve / veya bir MOC'ye eklemeden önce onlar için ilişkiler oluşturduğumda garip davranışlar ve çökmeler görmeye başladım. Bunu WWDC'de bir Çekirdek Veri mühendisi ile konuştum ve ilişkili olmayan nesneler için API orada olsa da, bir MOC'nin nesneler tarafından gönderilen KVO bildirimlerine büyük ölçüde bağlı olduğu için onu kullanmamasını şiddetle tavsiye ettiğini söyledi. Çok daha güvenli olduğu için geçici nesneler için normal NSObject kullanmayı önerdi.
Adrian Schönig

7
Bu, özellikle devam eden ilişkilerde iOS 8 ile iyi çalışmıyor gibi görünüyor. Bunu başka biri doğrulayabilir mi?
Janum Trivedi

40

iOS5, Mike Weller'in cevabına daha basit bir alternatif sunuyor. Bunun yerine bir alt NSManagedObjectContext kullanın . NSNotificationCenter aracılığıyla trambolin ihtiyacını ortadan kaldırır

Bir alt bağlam oluşturmak için:

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

Ardından, alt bağlamı kullanarak nesnelerinizi oluşturun:

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

Değişiklikler yalnızca alt bağlam kaydedildiğinde uygulanır. Yani değişiklikleri atmak için sadece kaydetmeyin.

İlişkilerde hala bir sınırlama var. yani, diğer bağlamlardaki nesnelerle ilişki kuramazsınız. Bunu aşmak için, nesneyi alt bağlamdan almak için nesne kimliklerini kullanın. Örneğin.

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

Alt bağlamı kaydetmenin, değişiklikleri üst içeriğe uyguladığını unutmayın. Üst bağlamı kaydetmek değişiklikleri devam ettirir.

Tam bir açıklama için WWDC 2012 oturumu 214'e bakın .


1
Bunu önerdiğiniz için teşekkürler! Bu yöntemi sıfır bağlamı kullanarak test eden bir demo yazdım ve en azından OSX'te, bu işe yaradı, bir nil bağlamı eklerken kaydetme sırasında özniteliklerini kaybetti - github.com/seltzered/CoreDataMagicalRecordTempObjectsDemo
Vivek Gani

mocÜçüncü kod parçacığında hangisi var ? Öyle mi childContextyoksa myMangedObjectContext?
bugloaf


bu çözüm sıfır bağlamına sahip olmaktan daha iyidir.
Will Y

Yana NSManagedObjectZaten ilgili sağlamaktadır NSManagedObjectContext: bir bağlamda seçimi otomatik hale getirebilirsiniz NSManagedObject* objectRelatedContextually = [objectWithRelationship.managedObjectContext objectWithID:objectRelated.objectID];sonra ve objectWithRelationship.relationship = objectRelatedContextually;.
Gary

9

Bu tür bir şeyi başarmanın doğru yolu, yeni bir yönetilen nesne bağlamıdır. Aynı kalıcı depo ile yönetilen bir nesne bağlamı oluşturursunuz:

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

Sonra yeni nesneler eklersiniz, onları değiştirirsiniz vb.

Kaydetme zamanı geldiğinde, tempContext üzerinde [tempContext save: ...] çağırmanız ve bunu orijinal içeriğinizle birleştirmek için kaydetme bildirimini işlemeniz gerekir. Nesneleri atmak için, sadece bu geçici içeriği serbest bırakın ve unutun.

Dolayısıyla, geçici bağlamı kaydettiğinizde, değişiklikler mağazada kalır ve yalnızca bu değişiklikleri ana bağlamınıza geri getirmeniz gerekir:

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

Bu aynı zamanda çok iş parçacıklı çekirdek veri işlemlerini gerçekleştirmeniz gereken yoldur. İş parçacığı başına bir bağlam.

Mevcut nesnelere bu geçici bağlamdan erişmeniz gerekiyorsa (ilişkiler eklemek vb. İçin), bunun gibi yeni bir örnek almak için nesnenin kimliğini kullanmanız gerekir:

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

Eğer bir NSManagedObjectyanlış bağlamda kullanmaya çalışırsanız , kaydederken istisnalar alırsınız.


Sadece bunun için ikinci bir bağlam oluşturmak çok israftır çünkü ayakta durmak NSManagedObjectContexthem bellek hem de CPU açısından pahalıdır. Bunun aslında bazı Apple örneklerinde olduğunu fark ettim, ancak bu örnekleri güncellediler ve düzelttiler.
Marcus S. Zarra

2
Apple, CoreDataBooks örnek kodu için hala bu tekniği kullanıyor (ikinci bir yönetilen nesne bağlamı oluşturuyor).
nevan kralı

1
Not Apple, CoreDataBooks'u güncelledi, aslında hala iki bağlam kullanıyor, ancak şimdi 2. bağlam, ilkinin alt öğesi. Bu teknik WWDC 2011 sunumu 303'te (iOS'taki Temel Verilerdeki yenilikler) tartışılmış (ve tavsiye edilmiştir) ve burada bahsedilmiştir (değişiklikleri yukarı doğru birleştirmek için çok, ÇOK, daha basit kodla) stackoverflow.com/questions/9791469/…
Rhubarb

4
"NSManagedObjectContext'i ayakta tutmak hem bellek hem de CPU açısından pahalı olduğundan, sadece bunun için ikinci bir bağlam oluşturmak çok israftır." . Hayır değil. Kalıcı mağaza koordinatörünün bağımlılıkları (yönetilen nesne modeli ve somut depolar) bağlam değildir. Bağlamlar hafiftir.
2014

3
@quellish Kabul edildi. Apple, WWDC'deki son temel veri performansı görüşmelerinde bağlam oluşturmanın çok hafif olduğunu belirtti.
Jesse

9

Sıfır bağlamından geçici nesneler oluşturmak, bağlamı! = Nil! Olan bir nesneyle gerçekten bir ilişki kurmaya çalışana kadar iyi çalışır.

bununla sorun olmayacağından emin ol.


Ben buna uygun değilim
Charlie

8

Açıkladığınız şey tam olarak ne için NSManagedObjectContextolduğu.

Gönderen Çekirdek Veri Temelleri: Çekirdek Veri Programlama Rehberi

Yönetilen bir nesne bağlamını akıllı bir not defteri olarak düşünebilirsiniz. Kalıcı bir mağazadan nesneleri aldığınızda, geçici kopyaları bir nesne grafiği (veya bir nesne grafikleri koleksiyonu) oluşturdukları karalama defterine getirirsiniz. Daha sonra bu nesneleri istediğiniz gibi değiştirebilirsiniz. Ancak bu değişiklikleri gerçekten kaydetmediğiniz sürece kalıcı mağaza değişmeden kalır.

Ve Temel Veri Programlama Kılavuzu: Yönetilen Nesne Doğrulama

Bu aynı zamanda bir "karalama defteri" temsil eden yönetilen bir nesne bağlamı fikrinin temelini oluşturur - genel olarak yönetilen nesneleri karalama defterine getirebilir ve nihayetinde değişiklikleri gerçekleştirmeden veya atmadan önce dilediğiniz gibi düzenleyebilirsiniz.

NSManagedObjectContexts hafif olacak şekilde tasarlanmıştır. Onları istediğiniz zaman oluşturabilir ve atabilirsiniz - kalıcı mağaza koordinatörü ve "ağır" olan bağımlılıklarıdır. Tek bir kalıcı mağaza koordinatörü, kendisiyle ilişkilendirilmiş birçok bağlama sahip olabilir. Daha eski, eski iş parçacığı sınırlandırma modeli altında bu, her bağlamda aynı kalıcı depo koordinatörünün ayarlanması anlamına gelir. Bugün bu, iç içe geçmiş bağlamları kalıcı mağaza koordinatörü ile ilişkili bir kök içeriğe bağlamak anlamına gelir.

Bir bağlam oluşturun, bu bağlam içinde yönetilen nesneleri oluşturun ve değiştirin. Onları sürdürmek ve bu değişiklikleri iletmek istiyorsanız, içeriği kaydedin. Aksi takdirde atın.

An'dan bağımsız yönetilen nesneler yaratmaya çalışmak, NSManagedObjectContextsorun istemektir. Temel Verilerin nihayetinde bir nesne grafiği için bir değişiklik izleme mekanizması olduğunu unutmayın. Bu nedenle, yönetilen nesneler gerçekten yönetilen nesne bağlamının bir parçasıdır . Bağlam , yaşam döngülerini gözlemler ve bağlam olmadan yönetilen nesnenin tüm işlevselliği doğru çalışmaz.


6

Geçici nesneyi kullanımınıza bağlı olarak, yukarıdaki tavsiyelerde bazı uyarılar vardır. Benim kullanım durumum, geçici bir nesne oluşturmak ve onu görünümlere bağlamak istememdir. Kullanıcı bu nesneyi kaydetmeyi seçtiğinde, mevcut nesne (ler) ile ilişki kurmak ve kaydetmek istiyorum. Bu değerleri tutmak için geçici bir nesne oluşturmaktan kaçınmak için bunu yapmak istiyorum. (Evet, kullanıcı kaydedinceye kadar bekleyebilir ve ardından görüntü içeriğini alabilirim ancak bu görünümleri bir masanın içine koyuyorum ve bunu yapma mantığı daha az zarif.)

Geçici nesneler için seçenekler şunlardır:

1) (Tercihli) Geçici nesneyi bir alt bağlamda oluşturun. Bu işe yaramayacak çünkü nesneyi kullanıcı arayüzüne bağlıyorum ve nesne erişimcilerin alt bağlamda çağrılacağını garanti edemiyorum. (Aksini belirten bir belge bulamadım, bu yüzden varsaymam gerekiyor.)

2) Geçici nesneyi sıfır nesne bağlamıyla oluşturun. Bu çalışmaz ve veri kaybına / bozulmasına neden olur.

Çözümüm: Bunu geçici nesneyi sıfır nesne bağlamı ile oluşturarak çözdüm ancak nesneyi # 2 olarak eklemek yerine kaydettiğimde tüm niteliklerini ana bağlamda oluşturduğum yeni bir nesneye kopyalıyorum. NSManagedObject alt sınıfımda cloneInto adında bir destekleme yöntemi oluşturdum: bu, herhangi bir nesne için öznitelikleri ve ilişkileri kolayca kopyalamama izin veriyor.


Ben de bunu arıyorum. Ama şüphem, ilişki niteliklerini nasıl idare edeceğinizdir?
Mani

1

Benim için Marcus'un cevabı işe yaramadı. İşte benim için çalıştı:

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

sonra, kaydetmeye karar verirsem:

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

Ayrıca onu serbest bırakmayı da unutmamalıyız

[unassociatedObject release]

1

Bunu yeniden yazmayı am cevabı bu soruya hızlı yönlendirme için tüm benzer soru olarak Swift için.

Aşağıdaki kodu kullanarak herhangi bir ManagedContext olmadan nesneyi bildirebilirsiniz.

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

Daha sonra nesneyi kaydetmek için içeriğe ekleyebilir ve kaydedebilirsiniz.

myContext.insert(unassociatedObject)
// Saving the object
do {
    try self.stack.saveContext()
    } catch {
        print("save unsuccessful")
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.