İOS 6'da tamamlama blokları için dispatch_get_current_queue () alternatifleri?


101

Bir blok ve bir tamamlama bloğunu kabul eden bir yöntemim var. İlk blok arka planda çalışmalı, tamamlama bloğu ise yöntemin çağrıldığı kuyrukta çalışmalıdır.

İkincisi için her zaman kullandım dispatch_get_current_queue(), ancak iOS 6 veya sonraki sürümlerde kullanımdan kaldırılmış gibi görünüyor. Onun yerine ne kullanmalıyım?


neden dispatch_get_current_queue()iOS 6'da kullanımdan kaldırıldı diyorsunuz ? dokümanlar bu konuda hiçbir şey söylemiyor
jere

3
Derleyici bundan şikayet ediyor. Dene.
cfischer

4
@jere Başlık dosyasını kontrol edin, kullanımdan kaldırıldığını belirtiyor
WDUK

En iyi uygulamanın ne olduğuna dair tartışmaların yanı sıra, soruyu yanıtlayabilecek [NSOperationQueue currentQueue] 'yu görüyorum. Kullanımıyla ilgili uyarılardan emin değilim.
Matt

bulunan uyarı ------ [NSOperationQueue currentQueue] dispatch_get_current_queue () ile aynı değildir ----- Bazen boş döndürür ---- dispatch_async (dispatch_get_global_queue (0, 0), ^ {NSLog (@ "q (0, 0)% @ ", dispatch_get_current_queue ()); NSLog (@" cq (0,0),% @ ", [NSOperationQueue currentQueue]);}); ----- q (0,0) <OS_dispatch_queue_root: com.apple.root.default-qos [0x100195140]> cq (0,0) (null) ----- sınırlandırılmış veya dispatch_get_current_queue () görünüyor Ben her koşulda geçerli sıranızı bildirdikleri için bkz tek çözüm olmaya
godzilla

Yanıtlar:


64

"Arayanın hangi sırada olduğu kuyruğa devam et" modeli çekici, ancak sonuçta harika bir fikir değil. Bu kuyruk, düşük öncelikli bir kuyruk, ana kuyruk veya garip özelliklere sahip başka bir kuyruk olabilir.

Buna en sevdiğim yaklaşım, "tamamlama bloğu şu özelliklere sahip bir uygulama tanımlı kuyrukta çalışır: x, y, z" demek ve eğer arayan kişi bundan daha fazla kontrol istiyorsa bloğun belirli bir kuyruğa gönderilmesine izin vermektir. Belirtilecek tipik bir özellik kümesi, "herhangi bir uygulama tarafından görülebilen kuyruğa göre seri, evresel olmayan ve zaman uyumsuz" gibi bir şey olacaktır.

** DÜZENLE **

Catfish_Man aşağıdaki yorumlara bir örnek koydu, ben sadece cevabına ekliyorum.

- (void) aMethodWithCompletionBlock:(dispatch_block_t)completionHandler     
{ 
    dispatch_async(self.workQueue, ^{ 
        [self doSomeWork]; 
        dispatch_async(self.callbackQueue, completionHandler); 
    } 
}

7
Tamamen kabul etti. Apple'ın her zaman bunu takip ettiğini görebilirsiniz; Ana kuyrukta bir şey yapmak istediğinizde, her zaman ana kuyruğa göndermeniz gerekir çünkü apple her zaman farklı bir iş parçacığında olduğunuzu garanti eder. Çoğu zaman uzun süren bir işlemin verileri getirmeyi / değiştirmeyi bitirmesini bekliyorsunuz ve ardından bunu tamamlama bloğunuzda arka planda işleyebilir ve ardından UI çağrılarını yalnızca ana kuyruktaki bir gönderim bloğuna yapıştırabilirsiniz. Ayrıca, geliştiriciler modele alışacağından, Apple'ın beklentiler açısından belirlediği şeyleri takip etmek her zaman iyidir.
Jack Lawrence

1
harika cevap .. ama ne söylediğinizi açıklamak için en azından bazı örnek kodlar bekliyordum
abbood

3
- (void) aMethodWithCompletionBlock: (dispatch_block_t) completeHandler {dispatch_async (self.workQueue, ^ {[self doSomeWork]; dispatch_async (self.callbackQueue, completeHandler);}}
Catfish_Man

(Tamamen önemsiz bir örnek için)
Catfish_Man

3
Genel durumda mümkün değildir, çünkü dispatch_sync () ve dispatch_set_target_queue () nedeniyle aynı anda birden fazla kuyrukta olmak mümkündür (ve aslında oldukça olasıdır). Genel durumun mümkün olan bazı alt kümeleri vardır.
Catfish_Man

27

Bu, temelde almayı açıkladığınız API için yanlış yaklaşımdır. Bir API bir engellemeyi ve çalıştırmak için bir tamamlama bloğunu kabul ederse, aşağıdaki gerçeklerin doğru olması gerekir:

  1. "Çalıştırmak için blok", dahili bir kuyrukta, örneğin API'ye özel ve dolayısıyla tamamen bu API'nin kontrolü altında olan bir kuyrukta çalıştırılmalıdır. Bunun tek istisnası, API'nin özellikle bloğun ana kuyrukta veya küresel eşzamanlı kuyruklardan birinde çalıştırılacağını bildirmesidir.

  2. Tamamlama bloğu her zaman bir demet (kuyruk, blok) olarak ifade edilmelidir , eğer # 1 için geçerli olan aynı varsayımlar geçerli değildir, örneğin tamamlama bloğu bilinen bir genel kuyrukta çalıştırılacaktır. Tamamlama bloğu ayrıca geçilen sırada eşzamansız olarak gönderilmelidir.

Bunlar sadece biçimsel noktalar değil, API'nizin kilitlenmelerden veya bir gün sizi en yakın ağaçtan asabilecek diğer uç durum davranışlarından korunmak için tamamen gereklidir. :-)


11
Kulağa mantıklı geliyor, ancak bazı nedenlerden dolayı Apple'ın kendi API'leri için yaklaşımı bu değil: bir tamamlama bloğu alan çoğu yöntem de sıraya girmiyor ...
cfischer

2
Doğru ve önceki iddiamı biraz değiştirmek gerekirse, tamamlama bloğunun ana kuyrukta veya küresel eşzamanlı kuyrukta çalıştırılacağı açıkça belliyse. Cevabımı belirtmek için değiştireceğim.
jkh

Apple'ın bu yaklaşımı benimsemediği konusunda yorum yapmak gerekirse: Apple, tanım başına her zaman "doğru" değildir. Uygun argümanlar her zaman herhangi bir bilim adamının onaylayacağı belirli bir otoriteden daha güçlüdür. Sanırım yukarıdaki yanıt, uygun bir yazılım mimarisi perspektifinden bunu çok iyi ifade ediyor.
Werner Altewischer

14

Diğer cevaplar harika, ama benim için cevap yapısal. Singleton'da böyle bir yöntemim var:

- (void) dispatchOnHighPriorityNonMainQueue:(simplest_block)block forceAsync:(BOOL)forceAsync {
    if (forceAsync || [NSThread isMainThread])
        dispatch_async_on_high_priority_queue(block);
    else
        block();
}

iki bağımlılığı vardır, bunlar:

static void dispatch_async_on_high_priority_queue(dispatch_block_t block) {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), block);
}

ve

typedef void (^simplest_block)(void); // also could use dispatch_block_t

Bu şekilde çağrılarımı diğer iş parçacığı üzerinde merkezileştiririm.


12

dispatch_get_current_queueÖncelikle kullanımınız konusunda dikkatli olmalısınız . Başlık dosyasından:

Yalnızca hata ayıklama ve günlük kaydı amaçları için önerilir:

Kodun kendi oluşturduğu global kuyruklardan biri veya bir kuyruk olmadığı sürece, kod, döndürülen kuyruk hakkında herhangi bir varsayımda bulunmamalıdır. Bu kuyruk dispatch_get_current_queue () tarafından döndürülen kuyruk değilse kod, bir kuyruğa eşzamanlı yürütmenin kilitlenmeye karşı güvenli olduğunu varsaymamalıdır.

İki şeyden birini yapabilirsiniz:

  1. Başlangıçta yayınladığınız kuyruğa bir referans tutun (eğer onu aracılığıyla oluşturduysanız dispatch_queue_create) ve o andan itibaren bunu kullanın.

  2. Aracılığıyla sistem tanımlı kuyrukları kullanın dispatch_get_global_queueve hangisini kullandığınızı takip edin.

Daha önce bulunduğunuz sırayı takip etmek için sisteme etkili bir şekilde güvenirken, bunu kendiniz yapmanız gerekecek.


16
dispatch_get_current_queue()Hangi kuyruğun olduğunu bulmak için kullanamazsak, "başlangıçta yayınladığınız sıraya bir referans tutabiliriz" ? Bazen hangi kuyruğun üzerinde çalıştığını bilmesi gereken kod, onun hakkında herhangi bir kontrole veya bilgiye sahip değildir. Bir arka plan kuyruğunda çalıştırılabilecek (ve çalıştırılması gereken) çok sayıda kodum var, ancak bazen gui'yi (ilerleme çubuğu, vb.) Güncellemem gerekiyor ve bu nedenle bu işlemler için ana kuyruğa dispatch_sync () göndermem gerekiyor. Zaten ana kuyruktaysa, dispatch_sync () sonsuza kadar kilitlenecektir. Bunun için kodumu yeniden düzenlemem aylarımı alacak.
Abhi Beckert

3
NSURLConnection'ın tamamlama geri aramalarını çağrıldığı aynı iş parçacığında verdiğini düşünüyorum. Geri arama sırasında kullanılmak üzere çağrıldığı kuyruğu depolamak için aynı "dispatch_get_current_queue" API'sini kullanıyor olabilir mi?
defactodeity

5

Apple kullanımdan kaldırıldı dispatch_get_current_queue(), ancak başka bir yerde bir boşluk bıraktı, bu nedenle mevcut gönderim kuyruğunu hala alabiliyoruz:

if let currentDispatch = OperationQueue.current?.underlyingQueue {
    print(currentDispatch)
    // Do stuff
}

Bu, en azından ana kuyruk için işe yarar. Bu underlyingQueuemülkün iOS 8'den beri kullanılabildiğini unutmayın .

Orijinal kuyrukta tamamlama blokunu gerçekleştirmeniz gerekiyorsa, OperationQueuedoğrudan da kullanabilirsiniz , yani GCD olmadan.



0

Bu benim de cevabım. Bu yüzden kullanım durumumuz hakkında konuşacağım.

Bir servis katmanımız ve UI katmanımız var (diğer katmanların yanı sıra). Hizmetler katmanı, görevleri arka planda çalıştırır. (Veri işleme görevleri, CoreData görevleri, Ağ çağrıları vb.). Hizmet katmanı, UI katmanının ihtiyaçlarını karşılamak için birkaç işlem kuyruğuna sahiptir.

UI katmanı, işini yapmak için hizmetler katmanına dayanır ve ardından bir başarı tamamlama bloğu çalıştırır. Bu bloğun içinde UIKit kodu olabilir. Basit bir kullanım örneği, sunucudan tüm mesajları almak ve koleksiyon görünümünü yeniden yüklemektir.

Burada, hizmetler katmanına aktarılan blokların, hizmetin çağrıldığı kuyrukta gönderileceğini garanti ediyoruz. Dispatch_get_current_queue kullanımdan kaldırılmış bir yöntem olduğundan, arayanın mevcut kuyruğunu almak için NSOperationQueue.currentQueue kullanırız. Bu mülkle ilgili önemli not.

Bu yöntemin çalışan bir işlemin bağlamının dışından çağrılması, genellikle nil döndürülmesiyle sonuçlanır.

Hizmetlerimizi her zaman bilinen bir kuyrukta (özel kuyruklarımız ve Ana sıramız) çağırdığımız için, bu bizim için iyi çalışıyor. ServisA'nın servisi arayabileceği ve servisi C arayabileceği durumlarımız var. İlk servis çağrısının nereden yapıldığını kontrol ettiğimiz için, diğer servislerin de aynı kurallara uyacağını biliyoruz.

Dolayısıyla, NSOperationQueue.currentQueue her zaman Kuyruklarımızdan birini veya MainQueue'yu döndürecektir.

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.