Bagaimana cara saya dispatch_sync, dispatch_async, dispatch_after, dll di Swift 3, Swift 4, dan seterusnya?

243

Saya memiliki banyak kode dalam proyek Swift 2.x (atau bahkan 1.x) yang terlihat seperti ini:

// Move to a background thread to do some long running work
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
    let image = self.loadOrGenerateAnImage()
    // Bounce back to the main thread to update the UI
    dispatch_async(dispatch_get_main_queue()) {
        self.imageView.image = image
    }
}

Atau hal-hal seperti ini untuk menunda eksekusi:

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.5 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
    print("test")
}

Atau segala jenis penggunaan lain dari Grand Central Dispatch API ...

Sekarang saya telah membuka proyek saya di Xcode 8 (beta) untuk Swift 3, saya mendapatkan semua jenis kesalahan. Beberapa dari mereka menawarkan untuk memperbaiki kode saya, tetapi tidak semua perbaikan menghasilkan kode yang berfungsi. Apa yang harus saya lakukan?

rickster
sumber

Jawaban:

343

Sejak awal, Swift telah menyediakan beberapa fasilitas untuk membuat ObjC dan C lebih cepat, menambahkan lebih banyak pada setiap versi. Sekarang, di Swift 3, "impor sebagai anggota" baru memungkinkan kerangka kerja dengan gaya tertentu dari C API - di mana Anda memiliki tipe data yang berfungsi seperti kelas, dan banyak fungsi global untuk bekerja dengannya - bertindak lebih seperti API Swift-asli. Jenis data yang diimpor sebagai kelas Swift, fungsi global terkaitnya mengimpor sebagai metode dan properti pada kelas tersebut, dan beberapa hal terkait seperti set konstanta dapat menjadi subtipe yang sesuai.

Dalam Xcode 8 / Swift 3 beta, Apple telah menerapkan fitur ini (bersama beberapa lainnya) untuk membuat kerangka Pengiriman jauh lebih cepat. (Dan Core Graphics , juga.) Jika Anda telah mengikuti upaya open-source Swift, ini bukan berita , tapi sekarang adalah pertama kalinya itu bagian dari Xcode.

Langkah pertama Anda untuk memindahkan proyek apa pun ke Swift 3 adalah dengan membukanya di Xcode 8 dan pilih Edit> Convert> To Current Swift Syntax ... di menu. Ini akan berlaku (dengan ulasan dan persetujuan Anda) semua perubahan sekaligus yang diperlukan untuk semua API yang diubah namanya dan perubahan lainnya. (Seringkali, satu baris kode dipengaruhi oleh lebih dari satu perubahan ini sekaligus, jadi menanggapi kesalahan perbaikan-nya secara individual mungkin tidak menangani semuanya dengan benar.)

Hasilnya adalah bahwa pola umum untuk memantulkan pekerjaan ke latar belakang dan kembali sekarang terlihat seperti ini:

// Move to a background thread to do some long running work
DispatchQueue.global(qos: .userInitiated).async {
    let image = self.loadOrGenerateAnImage()
    // Bounce back to the main thread to update the UI
    DispatchQueue.main.async {
        self.imageView.image = image
    }
}

Perhatikan bahwa kami menggunakan .userInitiatedalih-alih salah satu DISPATCH_QUEUE_PRIORITYkonstanta lama . Penentu Kualitas Layanan (QoS) diperkenalkan di OS X 10.10 / iOS 8.0, memberikan cara yang lebih jelas bagi sistem untuk memprioritaskan pekerjaan dan mencabut penentu prioritas lama. Lihat dokumen Apple tentang pekerjaan latar belakang dan efisiensi energi untuk detailnya.

Omong-omong, jika Anda menyimpan antrian sendiri untuk mengatur pekerjaan, cara mendapatkannya sekarang terlihat seperti ini (perhatikan bahwa itu DispatchQueueAttributesadalah OptionSet, jadi Anda menggunakan literal gaya koleksi untuk menggabungkan opsi):

class Foo { 
    let queue = DispatchQueue(label: "com.example.my-serial-queue",
                           attributes: [.serial, .qosUtility])
    func doStuff() {
        queue.async {
            print("Hello World")
        }
    }
}

Menggunakan dispatch_afteruntuk melakukan pekerjaan nanti? Itu adalah metode antrian juga, dan dibutuhkan DispatchTime, yang memiliki operator untuk berbagai jenis numerik sehingga Anda bisa menambahkan detik keseluruhan atau fraksional:

DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { // in half a second...
    print("Are we there yet?")
}

Anda dapat menemukan jalan di sekitar API Pengiriman baru dengan membuka antarmuka di Xcode 8 - gunakan Buka dengan Cepat untuk menemukan modul Pengiriman, atau letakkan simbol (seperti DispatchQueue) di proyek / taman bermain Swift Anda dan klik perintah, kemudian cari-cari di sekitar modul dari sana. (Anda dapat menemukan Swift Dispatch API di situs web Referensi API baru yang cerdik di Apple dan penampil dokumen dalam-Xcode, tetapi sepertinya konten dokumen dari versi C belum masuk ke dalamnya.)

Lihat Panduan Migrasi untuk tips lainnya.

rickster
sumber
3
Adapun Xcode 8 Beta 6, atribut .serial hilang dan perilaku default - forums.developer.apple.com/message/159457#159457
hyouuu
6
Ini membutuhkan pembaruan sejak XCode 8.1 .. label atribut telah menghilang dan sebagai gantinya kita dapat menggunakan 'DispatchQueue.global (qos: .background) .async'
Mike M
2
Jawaban yang bagus Benar-benar membantu saya memahami hal itu.
Mohsin Khubaib Ahmed
Saya harus menggunakan qos:bukannyaattributes:
Islam Q.
Bukankah itu seharusnya myQueue.async {dalam class Foocontoh?
vacawama
142

Di Xcode 8 beta 4 tidak berfungsi ...

Menggunakan:

DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
    print("Are we there yet?")
}

untuk async dua cara:

DispatchQueue.main.async {
    print("Async1")
}

DispatchQueue.main.async( execute: {
    print("Async2")
})
ingconti
sumber
Jadi itu tidak memblokir UI?
pengguna25
72

Ini adalah contoh yang bagus untuk Swift 4tentang async:

DispatchQueue.global(qos: .background).async {
    // Background Thread
    DispatchQueue.main.async {
        // Run UI Updates or call completion block
    }
}
S. Matsepura
sumber
hi DispatchQueue.main.async {// Jalankan Pembaruan UI} dijalankan sebelum utas latar belakang
Uma Achanta
mirip dengan coroutine Kotlin
user25
40

dalam penggunaan Xcode 8:

DispatchQueue.global(qos: .userInitiated).async { }
Marco
sumber
26

Cepat 5.2, 4 dan kemudian

Antrian Utama dan Latar Belakang

let main = DispatchQueue.main
let background = DispatchQueue.global()
let helper = DispatchQueue(label: "another_thread") 

Bekerja dengan async dan menyinkronkan utas!

 background.async { //async tasks here } 
 background.sync { //sync tasks here } 

Thread Async akan bekerja bersama dengan utas utama.

Utas sinkronisasi akan memblokir utas saat menjalankan.

Saranjith
sumber
1
Dan bagaimana Anda menggunakan thread Sync tanpa memblokir thread utama (UI) ?? Saya ingin mengeksekusi deretan hal di latar belakang - tetapi hal-hal ini harus dieksekusi satu demi satu secara sinkron. Selama ini UI harus tetap responsif .... Bagaimana Anda akan melakukan itu?
iKK
Gunakan NSOperationQueue. Yang setiap tugas Anda mewakili NSOperation. lihat stackoverflow.com/a/19746890/5215474
Saranjith
12

Swift 4.1 dan 5. Kami menggunakan antrian di banyak tempat dalam kode kami. Jadi, saya membuat kelas Threads dengan semua antrian. Jika Anda tidak ingin menggunakan kelas Threads Anda dapat menyalin kode antrian yang diinginkan dari metode kelas.

class Threads {

  static let concurrentQueue = DispatchQueue(label: "AppNameConcurrentQueue", attributes: .concurrent)
  static let serialQueue = DispatchQueue(label: "AppNameSerialQueue")

  // Main Queue
  class func performTaskInMainQueue(task: @escaping ()->()) {
    DispatchQueue.main.async {
      task()
    }
  }

  // Background Queue
  class func performTaskInBackground(task:@escaping () throws -> ()) {
    DispatchQueue.global(qos: .background).async {
      do {
        try task()
      } catch let error as NSError {
        print("error in background thread:\(error.localizedDescription)")
      }
    }
  }

  // Concurrent Queue
  class func perfromTaskInConcurrentQueue(task:@escaping () throws -> ()) {
    concurrentQueue.async {
      do {
        try task()
      } catch let error as NSError {
        print("error in Concurrent Queue:\(error.localizedDescription)")
      }
    }
  }

  // Serial Queue
  class func perfromTaskInSerialQueue(task:@escaping () throws -> ()) {
    serialQueue.async {
      do {
        try task()
      } catch let error as NSError {
        print("error in Serial Queue:\(error.localizedDescription)")
      }
    }
  }

  // Perform task afterDelay
  class func performTaskAfterDealy(_ timeInteval: TimeInterval, _ task:@escaping () -> ()) {
    DispatchQueue.main.asyncAfter(deadline: (.now() + timeInteval)) {
      task()
    }
  }
}

Contoh menunjukkan penggunaan antrian utama.

override func viewDidLoad() {
    super.viewDidLoad()
     Threads.performTaskInMainQueue {
        //Update UI
    }
}
Gurjinder Singh
sumber