Konkurensi di Swift dengan pemisahan perhatian

Operasi di Swift adalah cara ampuh untuk memisahkan tanggung jawab atas beberapa kelas sambil melacak kemajuan dan ketergantungan. Mereka secara resmi dikenal sebagai NSOperations dan digunakan bersama dengan OperationQueue.

Pastikan terlebih dahulu untuk membaca artikel saya tentang konkurensi di Swift sehingga Anda mengetahui dasar-dasar antrian dan pengiriman. Operasi memiliki banyak kesamaan dengan blok pengiriman tetapi memiliki beberapa manfaat lainnya. Mari selami!

Apa Itu Operasi di Swift?

Suatu operasi biasanya bertanggung jawab atas satu tugas sinkron. Ini adalah kelas abstrak dan tidak pernah digunakan secara langsung. Anda dapat menggunakan subkelas BlockOperation yang ditentukan sistem atau membuat subkelas Anda sendiri. Anda dapat memulai operasi dengan menambahkannya ke OperationQueue atau dengan memanggil metode start secara manual. Namun sangat disarankan untuk memberikan tanggung jawab penuh kepada OperationQueue untuk mengelola negara.

Memanfaatkan BlockOperation yang ditentukan sistem terlihat sebagai berikut:

let blockOperation = BlockOperation {
    print("Executing!")
}

let queue = OperationQueue()
queue.addOperation(blockOperation)

Hal ini juga dapat dilakukan dengan menambahkan blok secara langsung pada antrian:

queue.addOperation {
  print("Executing!")
}

Tugas yang diberikan akan ditambahkan ke OperationQueue yang akan memulai eksekusi sesegera mungkin.

Membuat Operasi Kustom

Anda menciptakan pemisahan kekhawatiran dengan operasi kustom. Anda dapat, misalnya, membuat penerapan khusus untuk mengimpor konten dan penerapan lainnya untuk mengunggah konten.

Contoh kode berikut menunjukkan subkelas khusus untuk mengimpor konten:

final class ContentImportOperation: Operation {

    let itemProvider: NSItemProvider

    init(itemProvider: NSItemProvider) {
        self.itemProvider = itemProvider
        super.init()
    }

    override func main() {
        guard !isCancelled else { return }
        print("Importing content..")
        
        // .. import the content using the item provider

    }
}

Kelas mengambil penyedia item dan mengimpor konten dalam metode utama. Fungsi main() adalah satu-satunya metode yang perlu Anda timpa untuk operasi sinkron. Tambahkan operasi ke antrean, dan atur blok penyelesaian untuk melacak penyelesaian:

let fileURL = URL(fileURLWithPath: "..")
let contentImportOperation = ContentImportOperation(itemProvider: NSItemProvider(contentsOf: fileURL)!)

contentImportOperation.completionBlock = {
    print("Importing completed!")
}

queue.addOperation(contentImportOperation)

// Prints:
// Importing content..
// Importing completed!

Ini memindahkan semua logika Anda untuk mengimpor konten ke dalam satu kelas tempat Anda dapat melacak kemajuan, penyelesaian, dan Anda dapat dengan mudah menulis tes.

Keadaan operasi yang berbeda

Suatu operasi dapat berada dalam beberapa status, bergantung pada status eksekusinya saat ini.

  • Siap:Siap untuk memulai
  • Eksekusi:Tugas sedang berjalan
  • Selesai:Setelah proses selesai
  • Dibatalkan:Tugas dibatalkan

Penting untuk mengetahui bahwa suatu operasi hanya dapat dijalankan satu kali. Setiap kali berada dalam status selesai atau dibatalkan, Anda tidak dapat lagi memulai ulang instance yang sama.

Dalam implementasi kustom, Anda perlu memeriksa status dibatalkan secara manual sebelum eksekusi untuk memastikan tugas dibatalkan. Ketahuilah bahwa perlombaan data dapat terjadi ketika suatu operasi dimulai dan dibatalkan pada saat yang bersamaan. Anda dapat membaca lebih lanjut tentang data race di postingan blog saya “Thread Sanitizer menjelaskan: Data Races di Swift.”

OperationQueue akan menghapus tugas secara otomatis dari antreannya setelah selesai, yang terjadi setelah eksekusi atau setelah pembatalan.

Memanfaatkan Ketergantungan

Manfaat menggunakan operasi adalah penggunaan dependensi. Anda dapat dengan mudah menambahkan ketergantungan antara dua instance. Misalnya, untuk mulai mengupload setelah konten diimpor:

let fileURL = URL(fileURLWithPath: "..")
let contentImportOperation = ContentImportOperation(itemProvider: NSItemProvider(contentsOf: fileURL)!)
contentImportOperation.completionBlock = {
    print("Importing completed!")
}

let contentUploadOperation = UploadContentOperation()
contentUploadOperation.addDependency(contentImportOperation)
contentUploadOperation.completionBlock = {
    print("Uploading completed!")
}

queue.addOperations([contentImportOperation, contentUploadOperation], waitUntilFinished: true)

// Prints:
// Importing content..
// Uploading content..
// Importing completed!
// Uploading completed!

Pengunggahan hanya akan dimulai setelah impor konten selesai. Ini tidak memperhitungkan pembatalan akun, yang berarti jika operasi impor dibatalkan, pengunggahan akan tetap dimulai.

Anda harus menerapkan pemeriksaan untuk melihat apakah dependensi dibatalkan atau tidak:

final class UploadContentOperation: Operation {
    override func main() {
        guard !dependencies.contains(where: { $0.isCancelled }), !isCancelled else {
            return
        }

        print("Uploading content..")
    }
}

Kesimpulan

Saya harap Anda bersemangat untuk mulai menerapkan operasi di Swift. Ini adalah permata tersembunyi yang memungkinkan Anda memisahkan masalah, menambahkan ketergantungan antar tugas, dan melacak penyelesaian.