Saya menguji beberapa kode yang melakukan pemrosesan asinkron menggunakan Grand Central Dispatch. Kode pengujian terlihat seperti ini:
[object runSomeLongOperationAndDo:^{
STAssert…
}];
Tes harus menunggu operasi selesai. Solusi saya saat ini terlihat seperti ini:
__block BOOL finished = NO;
[object runSomeLongOperationAndDo:^{
STAssert…
finished = YES;
}];
while (!finished);
Yang terlihat agak kasar, apakah Anda tahu cara yang lebih baik? Saya dapat membuka antrean lalu memblokir dengan menelepon dispatch_sync
:
[object runSomeLongOperationAndDo:^{
STAssert…
}];
dispatch_sync(object.queue, ^{});
... tapi itu mungkin mengekspos terlalu banyak pada object
.
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
denganwhile (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW)) { [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]]; }
Selain teknik semaphore yang dibahas secara mendalam dalam jawaban lain, kita sekarang dapat menggunakan XCTest di Xcode 6 untuk melakukan tes asinkron via
XCTestExpectation
. Ini menghilangkan kebutuhan untuk semaphores saat menguji kode asinkron. Sebagai contoh:Demi pembaca masa depan, sementara teknik semaphore pengiriman adalah teknik yang luar biasa ketika benar-benar dibutuhkan, saya harus mengakui bahwa saya melihat terlalu banyak pengembang baru, tidak terbiasa dengan pola pemrograman asinkron yang baik, tertarik terlalu cepat ke semafor sebagai mekanisme umum untuk membuat asinkron. rutinitas berperilaku serempak. Lebih buruk lagi saya telah melihat banyak dari mereka menggunakan teknik semaphore ini dari antrian utama (dan kita tidak boleh memblokir antrian utama dalam aplikasi produksi).
Saya tahu ini tidak terjadi di sini (ketika pertanyaan ini diposting, tidak ada alat yang bagus seperti
XCTestExpectation
; juga, di suite pengujian ini, kami harus memastikan tes tidak selesai sampai panggilan asinkron dilakukan). Ini adalah salah satu situasi langka di mana teknik semaphore untuk memblokir utas mungkin diperlukan.Jadi dengan permintaan maaf saya kepada penulis pertanyaan asli ini, untuk siapa teknik semafornya baik, saya menulis peringatan ini kepada semua pengembang baru yang melihat teknik semafor ini dan mempertimbangkan menerapkannya dalam kode mereka sebagai pendekatan umum untuk berurusan dengan asinkron. metode: Diperingatkan bahwa sembilan dari sepuluh, teknik semaphore tidakpendekatan terbaik saat menghadapi operasi asinkron. Alih-alih, biasakan diri Anda dengan pola blok penutupan / penutupan, serta pola dan notifikasi delegate-protokol. Ini sering merupakan cara yang jauh lebih baik untuk menangani tugas-tugas yang tidak sinkron, daripada menggunakan semafor untuk membuatnya berperilaku serempak. Biasanya ada alasan bagus bahwa tugas asinkronik dirancang untuk berperilaku asinkron, jadi gunakan pola asinkron yang tepat daripada mencoba membuatnya berperilaku sinkron.
sumber
NSOperationQueue
. Kecuali saya menggunakan sesuatu seperti semafor, unduhan dokumenNSOperation
akan segera muncul untuk menyelesaikan dan tidak akan ada antrian unduhan nyata - mereka akan melanjutkan banyak proses secara bersamaan, yang tidak saya inginkan. Apakah semafor masuk akal di sini? Atau ada cara yang lebih baik untuk membuat NSOperations menunggu akhir yang tidak sinkron dari yang lain? Atau sesuatu yang lain?AFHTTPRequestOperation
objek, maka Anda harus hanya membuat operasi penyelesaian (yang Anda akan bergantung pada operasi lain). Atau gunakan grup pengiriman. BTW, Anda mengatakan Anda tidak ingin mereka berjalan secara bersamaan, yang baik jika itu yang Anda butuhkan, tetapi Anda membayar penalti kinerja serius melakukan ini secara berurutan daripada secara bersamaan. Saya biasanya menggunakanmaxConcurrentOperationCount
4 atau 5.Saya baru saja datang ke masalah ini lagi dan menulis kategori berikut di
NSObject
:Dengan cara ini saya dapat dengan mudah mengubah panggilan tidak sinkron dengan panggilan balik menjadi panggilan sinkron:
sumber
Umumnya tidak menggunakan jawaban ini, mereka sering tidak akan skala (ada pengecualian di sana-sini, tentu saja)
Pendekatan-pendekatan ini tidak sesuai dengan bagaimana GCD dimaksudkan untuk bekerja dan pada akhirnya akan menyebabkan kebuntuan dan / atau membunuh baterai dengan polling nonstop.
Dengan kata lain, atur ulang kode Anda sehingga tidak ada yang sinkron menunggu hasil, tetapi alih-alih berurusan dengan hasil yang diberitahukan tentang perubahan status (mis. Protokol panggilan balik / delegasi, tersedia, pergi, kesalahan, dll.). (Ini dapat di refactored menjadi blok jika Anda tidak suka panggilan balik neraka.) Karena ini adalah bagaimana mengekspos perilaku nyata ke seluruh aplikasi daripada menyembunyikannya di balik façade palsu.
Sebagai gantinya, gunakan NSNotificationCenter , tentukan protokol delegasi khusus dengan callback untuk kelas Anda. Dan jika Anda tidak suka mucking dengan callback delegate seluruh, bungkus mereka menjadi kelas proxy konkret yang mengimplementasikan protokol khusus dan menyimpan berbagai blok di properti. Mungkin juga memberikan kemudahan konstruktor juga.
Pekerjaan awal sedikit lebih tetapi itu akan mengurangi jumlah kondisi ras yang mengerikan dan pemilihan baterai yang mematikan dalam jangka panjang.
(Jangan meminta contoh, karena itu sepele dan kami harus menginvestasikan waktu untuk mempelajari dasar-dasar obyektif juga.)
sumber
Inilah trik bagus yang tidak menggunakan semaphore:
Apa yang Anda lakukan adalah menunggu menggunakan
dispatch_sync
dengan blok kosong untuk Sinkron menunggu pada antrian pengiriman serial sampai blok A-Synchronous telah selesai.sumber
Contoh penggunaan:
sumber
Ada juga SenTestingKitAsync yang memungkinkan Anda menulis kode seperti ini:
(Lihat artikel objc.io untuk detailnya.) Dan karena Xcode 6 ada
AsynchronousTesting
kategoriXCTest
yang memungkinkan Anda menulis kode seperti ini:sumber
Ini adalah alternatif dari salah satu tes saya:
sumber
NSCondition
dokumentasi untuk-waitUntilDate:
"Anda harus mengunci penerima sebelum memanggil metode ini." Jadi-unlock
seharusnya setelah-waitUntilDate:
.Ini berhasil untuk saya.
sumber
Terkadang, Timeout loops juga membantu. Bolehkah Anda menunggu sampai Anda mendapatkan sinyal (mungkin BOOL) dari metode panggilan balik async, tetapi bagaimana jika tidak ada respons, dan Anda ingin keluar dari loop itu? Berikut ini solusinya, kebanyakan dijawab di atas, tetapi dengan tambahan Timeout.
sumber
Solusi yang sangat primitif untuk masalah ini:
sumber
Swift 4:
Gunakan
synchronousRemoteObjectProxyWithErrorHandler
alih-alihremoteObjectProxy
saat membuat objek jarak jauh. Tidak perlu lagi semafor.Contoh di bawah ini akan mengembalikan versi yang diterima dari proxy. Tanpa
synchronousRemoteObjectProxyWithErrorHandler
itu akan crash (mencoba mengakses memori yang tidak dapat diakses):sumber
Saya harus menunggu sampai UIWebView dimuat sebelum menjalankan metode saya, saya bisa mendapatkan ini bekerja dengan melakukan pemeriksaan siap UIWebView pada utas utama menggunakan GCD dalam kombinasi dengan metode semafor yang disebutkan dalam utas ini. Kode akhir terlihat seperti ini:
sumber