22

Bazı ağır kaldırma işlemleri için uygulamamda Grand Central Dispatch (GCD) kullanıyorum. Uygulama veri depolama amacıyla Core-Data kullanıyor. İşte benim senaryo (ilgili soru ile birlikte) var:CoreData ile Grand Central Dispatch (GCD)

dispatch_queue_t main_queue = dispatch_get_main_queue(); 
dispatch_queue_t request_queue = dispatch_queue_create("com.app.request", NULL); 

dispatch_async(request_queue, ^{ 
    MyNSManagedObject *mObject = [self.fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]]; 

    // … 
    // <heavy lifting> 
    // … 

    // … 
    // <update mObject> 
    // … 

    [self saveManagedObjectContext]; 
});  

[self saveManagedObjectContext] sonucunda, fetchResultsController temsilci yöntemleri otomatik olarak adlandırılır. Sonuç olarak, UI updation mantığı başladı.

Artık sorum şu -saveManagedObjectContext için main_queue kullanmam gerekiyor mu? main_queue içinde NSManagedObject benim tüm işlemleri gerçekleştirmeli miyim? NSManagedObject'u güncelleyen işlemlerin bazıları 2-3 saniye sürebilir. Tavsiye lütfen.

cevap

17

Muhtemelen bildiğiniz veya farkettiğiniz gibi ana iş parçacığı üzerinde UI işlemleri gerçekleştirmelisiniz. Bahsettiğiniz gibi UI güncellemesini kaydettiğinizde gerçekleşir. Ana iş parçacığına bir çağrıyı dispatch_sync numaralı numaraya yerleştirerek çözebilirsiniz.

dispatch_queue_t main_queue = dispatch_get_main_queue(); 
dispatch_queue_t request_queue = dispatch_queue_create("com.app.request", NULL); 

__block __typeof__(self) blockSelf = self; 

dispatch_async(request_queue, ^{ 
    MyNSManagedObject *mObject = [blockSelf.fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]]; 

    // update and heavy lifting... 

    dispatch_sync(main_queue, ^{ 
     [blockSelf saveManagedObjectContext]; 
    }); 
});  

blockSelfkullanımı yanlışlıkla döngüleri referans oluşturmak kaçınmaktır. (Practical blocks)

Çekirdek Veriler bir iş parçacığı başına Nesne Bağlam Yönetilen gerektirir yana
59

Çekirdek Veriler - iş parçacığı başına bir Yönetilen Nesne Bağlamı söz konusu olduğunda altın bir kural vardır. Yönetilen nesne bağlamları iş parçacığı güvenli değildir, bu nedenle arka plan görevinde çalışıyorsanız, ana iş parçacığını UI işlemleriyle iş parçacıklarının çakışmasını önlemek için kullanın veya işi yapmak için yeni bir bağlam oluşturun. Eğer iş işe yarayacaksa Birkaç saniye sonra UI'nizi kilitlemek için ikincisini yapmalısınız. Bunu yapmak için

yeni bağlam oluşturmak ve ana bağlam olarak bunu aynı kalıcı depo vermek: Yapmanız gereken

NSManagedObjectContext *backgroundContext = [[[NSManagedObjectContext alloc] init] autorelease]; 
[backgroundContext setPersistentStoreCoordinator:[mainContext persistentStoreCoordinator]]; 

Do olursa olsun operasyonlarını, yeni bağlam idare gerektiğini tasarruf sonra ne zaman Bildirimi kaydedin ve değişiklikleri ana içeriğinize mergeChangesFromContextDidSaveNotification: mesajıyla birleştirin. kaydetmek notifcation Handling

/* Save notification handler for the background context */ 
- (void)backgroundContextDidSave:(NSNotification *)notification { 
    /* Make sure we're on the main thread when updating the main context */ 
    if (![NSThread isMainThread]) { 
     [self performSelectorOnMainThread:@selector(backgroundContextDidSave:) 
           withObject:notification 
          waitUntilDone:NO]; 
     return; 
    } 

    /* merge in the changes to the main context */ 
    [self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification]; 
} 

/* ... */ 

/* Save the background context and handle the save notification */ 
[[NSNotificationCenter defaultCenter] addObserver:self 
             selector:@selector(backgroundContextDidSave:) 
              name:NSManagedObjectContextDidSaveNotification 
              object:backgroundContext]; 

[backgroundContext save:NULL]; 

[[NSNotificationCenter defaultCenter] removeObserver:self 
               name:NSManagedObjectContextDidSaveNotification 
               object:syncContext]; 

ve birleştirme aksi takdirde ana UI/bağlam değişiklikleri yapıldıkları görmezsiniz önemlidir: Kod şöyle görünmelidir. Birleştirerek ana getirmeResultsController vb. Etkinliklerinizi değiştirecek ve kullanıcı arayüzünü beklediğiniz gibi güncelleyecektir.

Dikkat edilmesi gereken diğer önemli bir nokta da, NSManagedObject örneklerinin yalnızca alındıkları bağlamda kullanılabilmesidir. İşleminizin bir nesneye başvurması gerekiyorsa, nesneyi objectID işlemine geçirmeniz ve existingObjectWithID:'u kullanarak yeni bağlamdan bir NSManagedObject örneğini yeniden getirmeniz gerekir. Yani bir şey gibi:

/* This can only be used in operations on the main context */ 
MyNSManagedObject *objectInMainContext = 
    [self.fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]]; 

/* This can now be used in your background context */ 
MyNSManagedObject *objectInBackgroundContext = 
    (MyNSManagedObject *) [backgroundContext existingObjectWithID:[objectInMainContext objectID]]; 
+1

Peki, benim söylediklerimde, fetchedResultsController kullanmak yerine, yeni bir yönetilen nesne bağlamı oluşturmalıyım (backgro) undManagedObjectContext), gerekli yönetilen nesneyi getirin, gerekli işlemleri gerçekleştirin, yönetilen nesneyi güncelleyin, yönetilen bu nesne bağlamını (backgroundManagedObjectContext) kaydedin ve değişiklikleri ana yönetilen nesne bağlamında yansıtacak şekilde birleştirin. Bu hayatımı çok sefil hale getirecek. – Mustafa

+0

UI'nizdeki öğeleri görüntülemek ve güncellemek için hala getirilen sonuç denetleyicisini kullanabilirsiniz. Size gösterdiğim kod, ihtiyacınız olan herhangi bir işlemi ayrı bir içerikte gerçekleştirmek için gereken her şeydir, başka hiçbir şey değişmemelidir. –

+0

Bu altın kuralın etrafında yollar vardır: https://github.com/adam-roth/coredata-threadsafe. – aroth

0

, olası bir çözüm, küresel bir yöneticisinde Konu başına bir bağlam izlemek sonra bildirimlerini kaydetmek izlemek ve tüm Konular yaymak olacaktır:

varsayarsak : Burada

@property (nonatomic, strong) NSDictionary* threadsDictionary; 

(iş parçacığı başına) yönetilen nesne almak için yapılması gerekenler:

- (NSManagedObjectContext *) managedObjectContextForThread { 

// Per thread, give one back 
NSString* threadName = [NSString stringWithFormat:@"%d",[NSThread currentThread].hash]; 

NSManagedObjectContext * existingContext = [self.threadsDictionary objectForKey:threadName]; 
if (existingContext==nil){ 
    existingContext = [[NSManagedObjectContext alloc] init]; 
    [existingContext setPersistentStoreCoordinator: [self persistentStoreCoordinator]]; 
    [self.threadsDictionary setValue:existingContext forKey:threadName]; 
} 

return existingContext; 

} Global yöneticinin init yönteminde bir noktada

(I a tek kullanılır): Sonra bildirimlerini kaydetmek almak ve diğer tüm yönetilen içeriğe yaymak için

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(backgroundContextDidSave:)             name:NSManagedObjectContextDidSaveNotification             object:nil]; 

nesneleri:

- (void)backgroundContextDidSave:(NSNotification *)notification { 
    /* Make sure we're on the main thread when updating the main context */ 
    if (![NSThread isMainThread]) { 
     [self performSelectorOnMainThread:@selector(backgroundContextDidSave:) 
           withObject:notification 
          waitUntilDone:NO]; 
     return; 
    } 

    /* merge in the changes to the main context */ 
    for (NSManagedObjectContext* context in [self.threadsDictionary allValues]){ 
      [context mergeChangesFromContextDidSaveNotification:notification]; 
    } 
} 

(bazı diğer yöntemler netlik için kaldırılmıştır)

İlgili konular