Pengiraan Swift tertunda mengekalkan pemiliknya apabila tugasnya menangkap pemilik itu secara kuat. Gunakan tangkapan lemah supaya pemilik boleh dinyahperuntukkan semasa menunggu, atau tangkap petikan nilai bebas apabila pengiraan perlu selesai tanpanya.
Bagi pembangun Mac yang membina pratonton pengiraan desktop kecil, tugas praktikalnya ialah memilih jangka hayat itu dengan sengaja. Contoh ini menunggu seketika, menambah nilai sintetik [4, 8, 12], dan memformatkan jumlahnya. Semuanya berjalan secara setempat dalam ingatan.
Apakah jangka hayat yang patut dimiliki oleh pengiraan anda?
Senarai tangkapan Swift membolehkan anda menentukan perkara yang dikekalkan oleh penutupan. Tangkapan kuat mengekalkan tika yang dirujuk; rujukan lemah membolehkan tika itu dinyahperuntukkan dan menjadi nil selepas itu. Ini ialah tingkah laku bahasa yang didokumenkan dan tidak bergantung pada tempoh pengiraan menunggu. Pengiraan Rujukan Automatik
Pilih kontrak sebelum memilih sintaks tangkapan. Jadual keputusan berikut menerapkan peraturan bahasa tersebut pada pratonton desktop tertunda:
| Tingkah laku yang dikehendaki | Strategi tangkapan | Apakah yang berlaku selepas pemanggil melepaskan pemiliknya? |
|---|---|---|
| Selesaikan menggunakan pemilik yang sama | [self] |
Tugas mengekalkan pemilik itu untuk kerjanya. |
| Teruskan hanya jika pemilik masih tersedia | [weak self] |
Selepas menunggu, tugas memeriksa sama ada pemilik itu masih wujud. |
| Selesaikan menggunakan nilai yang dipilih semasa dijadualkan | [samples = self.samples] |
Tugas mengekalkan nilai yang ditangkap tanpa mengekalkan pemilik ini. |
Baris petikan nilai mempunyai syarat penting: contoh ini menangkap tatasusunan integer. Ungkapan senarai tangkapan dinilai apabila penutupan dicipta. Menangkap tika kelas sebaliknya akan mengekalkan rujukan kepada tika itu; ia tidak akan menghasilkan salinan bebas bagi kandungan boleh ubahnya. Ungkapan
Terdapat juga persoalan berasingan tentang nilai. Versi kuat dan lemah di bawah membaca nilai pemilik selepas kelewatan. Versi petikan nilai menggunakan nilai ketika penjadualan. Jika input pratonton boleh berubah semasa menunggu, tentukan tafsiran yang patut diwakili oleh hasilnya.
Jalankan perbandingan setempat yang lengkap pada Mac anda
Gunakan macOS 13 atau lebih baharu dan rantaian alat Swift 6 atau lebih baharu yang telah dipasang. Lebihan beban Task.sleep(for:) berasaskan tempoh yang digunakan di sini didokumenkan untuk macOS 13 dan ke atas. Ia menggantung tugas tanpa menyekat utas asas dan melemparkan CancellationError jika pembatalan berlaku sebelum tempoh menunggu tamat. sleep(for:tolerance:clock:)
Simpan program lengkap berikut sebagai CaptureDemo.swift. Pengira, penutupan tugas dan demonstrasi berjalan pada @MainActor, yang mengekalkan akses kepada keadaannya secara bersiri. Swift mendokumenkan pengasingan aktor utama pada peringkat kelas serta anotasi penutupan eksplisit yang digunakan di sini. Keserentakan
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)
}
}
Aritmetik menggunakan reduce(0, +) pustaka standard untuk menggabungkan integer menjadi satu jumlah. NumberFormatter Foundation menukar jumlah itu kepada teks paparan; kaedah string(from:) mengembalikan rentetan pilihan, jadi program melaporkan kegagalan pemformatan secara jelas. Sampel tetap menghasilkan jumlah 24. reduce(::), NumberFormatter, string(from:)
Dalam Terminal, tukar ke folder yang mengandungi fail itu dan jalankan arahan ini. Sasaran tersebut menetapkan macOS 13 untuk seni bina pemproses Mac anda. Pilihan -parse-as-library membolehkan titik masuk @main eksplisit dalam fail itu. Pengkompil Swift menerima fail sumber dan pilihan baris arahan; swiftc -help menyenaraikan pilihan yang dipasang bersama rantaian alat anda. Manual baris arahan Swift
swift --version
swiftc -parse-as-library -swift-version 6 -target "$(uname -m)-apple-macos13.0" CaptureDemo.swift -o CaptureDemo
./CaptureDemo
Output aplikasi yang dijangka ditunjukkan di bawah. Output ini diterbitkan daripada peraturan tangkapan dan pembatalan; persekitaran penyelidikan manuskrip ini tidak mempunyai pengkompil Swift, jadi program itu diperiksa 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 memerhatikan pemilikan tanpa menambah rujukan kuat. Setiap demonstrasi mengosongkan rujukan kuatnya sendiri sebelum menunggu tugas. Oleh sebab demonstrasi dan penutupan tugas menggunakan aktor yang sama, penutupan yang baru dicipta tidak boleh berselang dengan persediaan segerak itu. Ini menjadikan perbandingan tidak bergantung pada tekaan tentang seberapa cepat tugas bermula. Keserentakan
Mengapakah kedudukan pemeriksaan lemah itu penting?
Dalam cabang lemah, guard let self muncul selepas kelewatan. Oleh itu, tugas boleh menunggu tanpa mengekalkan pemilik secara kuat. Jika pemilik itu sudah tiada, hasil eksplisit Owner unavailable menamatkan pengiraan tersebut.
Mengalihkan nyahbungkus ke sebelum kelewatan mengubah jangka hayat. Berikut ialah cebisan ilustrasi yang menggantikan badan penutupan cabang lemah; ini bukan program berasingan:
// 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 pilihan yang berjaya mewujudkan rujukan setempat kuat yang diperlukan semula selepas penggantungan. Akibatnya, pemilik kekal tersedia sepanjang tempoh menunggu sebaik sahaja pengikatan itu berjaya. Ini mengikuti peraturan rujukan kuat Swift: tangkapan lemah menentukan cara penutupan mula-mula mencapai pemilik, manakala pemboleh ubah setempat yang dinyahbungkus menentukan cara kod seterusnya mengekalkannya. Pengiraan Rujukan Automatik
Bagi kontrak “menunggu tanpa mengekalkan pemilik, kemudian mengira hanya jika pemilik masih wujud,” kekalkan nyahbungkus selepas penantian dan lakukan pengiraan ringkas sejurus selepas itu. Titik penggantungan tambahan selepas nyahbungkus memerlukan keputusan jangka hayat yang lain.
Tangkapan unowned memerlukan tika yang dirujuk kekal sah setiap kali diakses. Ia tidak menyediakan pemeriksaan ketiadaan pilihan yang diperlukan oleh contoh ini, yang memang bertujuan membolehkan pemilik dilepaskan semasa kelewatan. Pengiraan Rujukan Automatik
Adakah melepaskan pemegang tugas menghentikan pengiraan?
Tugas terus berjalan walaupun anda membuang pemegangnya. Menyimpan pemegang membolehkan anda menunggu hasilnya atau meminta pembatalan. Apple juga mendokumenkan bahawa tugas yang telah selesai segera melepaskan penutupan operasinya dan rujukan yang dipegang oleh penutupan itu. Oleh itu, tangkapan kuat yang terhad tempohnya memanjangkan jangka hayat pemilik sepanjang kerja tanpa semestinya menghasilkan kitaran rujukan yang berkekalan. Tugas
Pembatalan ialah kontrak yang berasingan daripada pemilikan. Demonstrasi terakhir meminta pembatalan melalui pemegang tersebut. Tidur yang melemparkan ralat dan Task.checkCancellation() selepasnya menyediakan tempat operasi boleh berhenti, lalu pemanggil melaporkan hasil itu. Pembatalan Swift bersifat kerjasama: permintaan pembatalan tidak menghentikan kod pengiraan sewenang-wenangnya secara paksa. Tugas
Jangan gantikan tidur yang melemparkan ralat dengan try? lalu teruskan tanpa syarat jika pembatalan sepatutnya menghalang hasil. Menyembunyikan ralat itu akan membolehkan pelaksanaan mencapai pengiraan melainkan pemeriksaan pembatalan lain menghentikannya. Begitu juga, menetapkan nil pada pemboleh ubah pemilik bukanlah permintaan pembatalan.
Untuk pratonton desktop, perbezaan ini memberikan tiga kawalan berguna: sama ada pemilik kekal diperuntukkan, nilai input yang digunakan oleh pengiraan, dan sama ada operasi patut dihentikan. Pilih setiap satunya secara jelas dan jangan mengharapkan tangkapan lemah melaksanakan ketiga-tiganya.
Apakah yang patut anda ubah untuk pratonton desktop?
Gunakan versi lemah apabila pratonton tertunda hanya berguna selagi pemiliknya masih tersedia. Gunakan versi petikan nilai apabila input sintetik yang dipilih patut menghasilkan keputusan secara bebas. Gunakan versi kuat apabila operasi itu sememangnya memerlukan pemilik untuk diselesaikan.
Jangka hayat objek juga berbeza daripada keterlihatan antara muka. Penutupan tetingkap dengan sendirinya tidak membuktikan bahawa setiap rujukan kuat kepada pengiranya telah dibuang. Jika aplikasi patut menghentikan kerja tertunda apabila pratonton ditutup, sambungkan peristiwa aplikasi itu kepada kaedah cancel() pada pemegang tugas yang disimpan; pengiraan itu tetap perlu bekerjasama dengan pembatalan. Tugas
Pastikan beban kerja contoh ini kecil. Pengasingan aktor utama menjadikan akses keadaan mudah, dan penantian itu menggantung tugas, tetapi jumlah segerak masih berjalan pada aktor tersebut. Panduan keserentakan Swift menerangkan bahawa aktor utama juga mengendalikan kerja antara muka. Pengiraan yang jauh lebih besar memerlukan reka bentuk pelaksanaan berasingan yang menghantar nilai yang sesuai kepada pekerja. Keserentakan
Bagi pengiraan setempat ini, ujiannya mudah: buang rujukan pemanggil sebelum operasi tertunda berjalan dan perhatikan hasilnya. Tangkapan kuat mengekalkan pemilik, tangkapan lemah membolehkan hasil eksplisit bahawa pemilik tidak tersedia, dan petikan tatasusunan integer mengekalkan input yang diperlukan sahaja untuk menyelesaikan kerja.
Sumber
- Pengiraan Rujukan Automatik — Swift.org
- Ungkapan — Swift.org
- Keserentakan — Swift.org
- Tugas — Apple Developer
- sleep(for:tolerance:clock:) — Apple Developer
- reduce(::) — Apple Developer
- NumberFormatter — Apple Developer
- string(from:) — Apple Developer
- Manual baris arahan Swift — Swift project