Perhitungan Swift yang tertunda membuat pemiliknya tetap hidup ketika tugasnya menangkap pemilik tersebut secara kuat. Gunakan tangkapan lemah agar pemiliknya dapat menghilang selama menunggu, atau tangkap snapshot nilai yang mandiri jika perhitungan harus selesai tanpanya.

Bagi pengembang Mac yang membuat pratinjau perhitungan desktop sederhana, tugas praktisnya adalah menentukan masa hidup tersebut dengan sengaja. Contoh ini menunggu sebentar, menjumlahkan nilai sintetis [4, 8, 12], lalu memformat totalnya. Semuanya berjalan secara lokal di memori.

Masa hidup seperti apa yang sebaiknya dimiliki perhitungan Anda?

Daftar tangkapan Swift memungkinkan Anda menentukan apa yang dipertahankan oleh sebuah closure. Tangkapan kuat mempertahankan instance yang dirujuk; referensi lemah memungkinkan instance tersebut di-dealokasi dan setelahnya menjadi nil. Perilaku bahasa ini terdokumentasi dan tidak bergantung pada lamanya perhitungan menunggu. Automatic Reference Counting

Tentukan kontraknya sebelum memilih sintaks tangkapan. Tabel keputusan berikut menerapkan aturan bahasa tersebut pada pratinjau desktop yang tertunda:

Perilaku yang diinginkan Strategi tangkapan Apa yang terjadi setelah pemanggil melepaskan pemiliknya?
Selesai menggunakan pemilik yang sama [self] Tugas mempertahankan pemilik agar tersedia untuk pekerjaannya.
Lanjut hanya jika pemilik masih tersedia [weak self] Setelah menunggu, tugas memeriksa apakah pemilik masih ada.
Selesai menggunakan nilai yang dipilih saat dijadwalkan [samples = self.samples] Tugas mempertahankan nilai yang ditangkap tanpa mempertahankan pemilik ini.

Baris snapshot memiliki syarat penting: contoh ini menangkap array bilangan bulat. Ekspresi dalam daftar tangkapan dievaluasi saat closure dibuat. Menangkap instance class sebagai gantinya akan mempertahankan referensi ke instance tersebut; itu tidak membuat salinan mandiri dari konten yang dapat berubah. Expressions

Ada pula pertanyaan terpisah tentang nilai. Versi kuat dan lemah di bawah membaca nilai pemilik setelah jeda. Versi snapshot menggunakan nilai saat penjadwalan. Jika masukan pratinjau dapat berubah selama menunggu, tentukan interpretasi mana yang seharusnya diwakili oleh hasilnya.

Jalankan perbandingan lokal lengkap di Mac Anda

Gunakan macOS 13 atau lebih baru dan toolchain Swift 6 atau lebih baru yang sudah terpasang. Overload Task.sleep(for:) berbasis durasi yang digunakan di sini terdokumentasi untuk macOS 13 ke atas. Overload ini menangguhkan tugas tanpa memblokir thread yang mendasarinya dan melempar CancellationError jika pembatalan terjadi sebelum waktu tunggu berakhir. sleep(for:tolerance:clock:)

Simpan program lengkap berikut sebagai CaptureDemo.swift. Kalkulator, closure tugas, dan demonstrasi berjalan di @MainActor, yang membuat akses ke status terserialisasi. Swift mendokumentasikan isolasi main actor di tingkat class serta anotasi closure eksplisit yang digunakan di sini. Concurrency

import Foundation

enum CapturePolicy: String {
    case strong, weak, snapshot
}

@MainActor
func formatTotal(_ samples: [Int]) -> String {
    let total = samples.reduce(0, +)
    let formatter = NumberFormatter()
    formatter.numberStyle = .decimal
    formatter.locale = Locale(identifier: "en_US_POSIX")
    formatter.usesGroupingSeparator = false
    formatter.maximumFractionDigits = 0

    guard let text = formatter.string(from: NSNumber(value: total)) else {
        return "Formatting unavailable"
    }
    return "Total: \(text)"
}

@MainActor
final class Calculator {
    var samples = [4, 8, 12]

    func start(_ policy: CapturePolicy) -> Task<String, Error> {
        switch policy {
        case .strong:
            return Task { @MainActor [self] in
                try await Task.sleep(for: .milliseconds(300))
                try Task.checkCancellation()
                return formatTotal(self.samples)
            }

        case .weak:
            return Task { @MainActor [weak self] in
                try await Task.sleep(for: .milliseconds(300))
                try Task.checkCancellation()
                guard let self else {
                    return "Owner unavailable"
                }
                return formatTotal(self.samples)
            }

        case .snapshot:
            return Task { @MainActor [samples = self.samples] in
                try await Task.sleep(for: .milliseconds(300))
                try Task.checkCancellation()
                return formatTotal(samples)
            }
        }
    }
}

@main
struct CaptureDemo {
    @MainActor
    static func demonstrate(
        _ policy: CapturePolicy,
        cancel: Bool = false
    ) async {
        var owner: Calculator? = Calculator()
        weak var probe = owner
        let task = owner!.start(policy)
        owner = nil

        let label = cancel ? "\(policy.rawValue), cancelled" : policy.rawValue
        print("\(label) retains owner: \(probe != nil)")

        if cancel {
            task.cancel()
        }

        do {
            let result = try await task.value
            print(result)
        } catch is CancellationError {
            print("Cancelled")
        } catch {
            print("Calculation failed: \(error)")
        }
    }

    @MainActor
    static func main() async {
        await demonstrate(.strong)
        await demonstrate(.weak)
        await demonstrate(.snapshot)
        await demonstrate(.snapshot, cancel: true)
    }
}

Operasi aritmetika menggunakan reduce(0, +) dari pustaka standar untuk menggabungkan bilangan bulat menjadi satu jumlah. NumberFormatter dari Foundation mengubah jumlah tersebut menjadi teks tampilan; metodenya, string(from:), mengembalikan string opsional, sehingga program melaporkan kegagalan pemformatan secara eksplisit. Contoh tetap ini menghasilkan 24. reduce(::), NumberFormatter, string(from:)

Di Terminal, pindah ke folder yang berisi file tersebut lalu jalankan perintah berikut. Target menentukan macOS 13 untuk arsitektur prosesor Mac Anda. Opsi -parse-as-library mengizinkan titik masuk @main eksplisit pada file ini. Kompilator Swift menerima file sumber dan opsi baris perintah; swiftc -help mencantumkan opsi yang tersedia bersama toolchain Anda. Swift command-line manual

swift --version
swiftc -parse-as-library -swift-version 6 -target "$(uname -m)-apple-macos13.0" CaptureDemo.swift -o CaptureDemo
./CaptureDemo

Keluaran aplikasi yang diharapkan ditampilkan di bawah. Keluaran ini diturunkan dari aturan tangkapan dan pembatalan; lingkungan riset naskah ini tidak memiliki kompilator Swift, jadi programnya ditinjau tetapi tidak dijalankan di sini.

strong retains owner: true
Total: 24
weak retains owner: false
Owner unavailable
snapshot retains owner: false
Total: 24
snapshot, cancelled retains owner: false
Cancelled

probe lemah mengamati kepemilikan tanpa menambahkan referensi kuat lain. Setiap demonstrasi menghapus referensi kuatnya sendiri sebelum menunggu tugas. Karena closure demonstrasi dan tugas menggunakan actor yang sama, closure yang baru dibuat tidak dapat berselang-seling dengan penyiapan sinkron tersebut. Dengan demikian, perbandingan ini tidak bergantung pada perkiraan seberapa cepat tugas mulai berjalan. Concurrency

Mengapa posisi pemeriksaan lemah itu penting?

Pada cabang lemah, guard let self muncul setelah jeda. Karena itu tugas dapat menunggu tanpa mempertahankan pemilik secara kuat. Jika pemilik sudah tidak ada, hasil eksplisit Owner unavailable mengakhiri perhitungan tersebut.

Memindahkan unwrap ke sebelum jeda mengubah masa hidupnya. Berikut fragmen ilustratif yang menggantikan isi closure pada cabang lemah; ini bukan program terpisah:

// Inside the existing [weak self] task closure:
guard let self else {
    return "Owner unavailable"
}
try await Task.sleep(for: .milliseconds(300))
try Task.checkCancellation()
return formatTotal(self.samples)

Di sini, pengikatan opsional yang berhasil membuat referensi lokal kuat yang diperlukan lagi setelah penangguhan. Akibatnya, setelah pengikatan itu berhasil, pemilik tetap tersedia selama waktu tunggu. Ini mengikuti aturan referensi kuat Swift: tangkapan lemah menentukan cara closure awalnya mengakses pemilik, sedangkan variabel lokal hasil unwrap menentukan cara kode selanjutnya mempertahankannya. Automatic Reference Counting

Untuk kontrak “menunggu tanpa mempertahankan pemilik, lalu menghitung hanya jika pemilik masih ada”, pertahankan unwrap setelah waktu tunggu dan segera lakukan perhitungan sederhana sesudahnya. Titik penangguhan tambahan setelah unwrap memerlukan keputusan masa hidup tersendiri.

Tangkapan unowned mensyaratkan instance yang dirujuk tetap valid setiap kali diakses. Tangkapan ini tidak menyediakan pemeriksaan opsional untuk kondisi tidak ada yang dibutuhkan contoh ini, karena pelepasan pemilik selama jeda memang disengaja. Automatic Reference Counting

Apakah melepaskan handle tugas menghentikan perhitungan?

Tugas terus berjalan meskipun handle-nya dibuang. Dengan menyimpan handle, Anda dapat menunggu hasilnya atau meminta pembatalan. Apple juga mendokumentasikan bahwa tugas yang selesai segera melepaskan closure operasinya beserta referensi yang ditahan closure tersebut. Jadi, tangkapan kuat yang berjangka terbatas memperpanjang masa hidup pemilik selama pekerjaan berlangsung tanpa harus membentuk siklus referensi yang bertahan lama. Task

Pembatalan merupakan kontrak yang terpisah dari kepemilikan. Demonstrasi terakhir meminta pembatalan melalui handle. Sleep yang melempar galat dan Task.checkCancellation() setelahnya menyediakan titik tempat operasi dapat berhenti, dan pemanggil melaporkan hasil tersebut. Pembatalan Swift bersifat kooperatif: memintanya tidak menghentikan kode perhitungan sembarang secara paksa. Task

Jangan mengganti sleep yang melempar galat dengan try? lalu melanjutkan tanpa syarat jika pembatalan seharusnya mencegah hasil. Menekan galat itu akan memungkinkan eksekusi mencapai perhitungan, kecuali pemeriksaan pembatalan lain menghentikannya. Demikian pula, menetapkan nil ke variabel pemilik bukanlah permintaan pembatalan.

Untuk pratinjau desktop, perbedaan ini memberi tiga kendali yang berguna: apakah pemilik tetap dialokasikan, nilai masukan mana yang digunakan perhitungan, dan apakah operasi harus dihentikan. Tentukan masing-masing secara eksplisit, alih-alih mengharapkan tangkapan lemah menjalankan ketiganya.

Apa yang sebaiknya Anda ubah untuk pratinjau desktop?

Gunakan versi lemah ketika pratinjau yang tertunda hanya berguna selama pemiliknya masih tersedia. Gunakan versi snapshot ketika masukan sintetis yang dipilih seharusnya menghasilkan keluaran secara mandiri. Gunakan versi kuat ketika penyelesaian operasi memang memerlukan pemilik.

Masa hidup objek juga berbeda dari visibilitas antarmuka. Jendela yang ditutup tidak dengan sendirinya membuktikan bahwa semua referensi kuat ke kalkulatornya telah dihapus. Jika aplikasi seharusnya menghentikan pekerjaan yang tertunda saat pratinjau ditutup, hubungkan peristiwa aplikasi tersebut ke metode cancel() pada handle tugas yang disimpan; perhitungan tetap harus bekerja sama dengan pembatalan. Task

Pertahankan beban kerja contoh ini tetap kecil. Isolasi main actor menyederhanakan akses ke status, dan waktu tunggunya menangguhkan tugas, tetapi penjumlahan sinkron tetap berjalan pada actor tersebut. Panduan konkurensi Swift menjelaskan bahwa main actor juga menangani pekerjaan antarmuka. Perhitungan yang jauh lebih besar memerlukan rancangan eksekusi terpisah yang meneruskan nilai yang sesuai ke worker. Concurrency

Untuk perhitungan lokal ini, pengujian penentunya sederhana: hapus referensi pemanggil sebelum operasi tertunda berjalan, lalu amati hasilnya. Tangkapan kuat mempertahankan pemilik, tangkapan lemah memungkinkan hasil eksplisit bahwa pemilik tidak tersedia, dan snapshot array bilangan bulat hanya mempertahankan masukan yang diperlukan untuk menyelesaikan perhitungan.

Sumber