Gecikmeli bir Swift hesaplaması, görevi sahibini güçlü biçimde yakaladığında sahibini yaşatır. Bekleme sırasında sahibin ortadan kalkmasına izin vermek için zayıf yakalama kullanın veya hesaplama sahibi olmadan tamamlanacaksa bağımsız bir değer anlık görüntüsü yakalayın.
Mac için küçük bir masaüstü hesaplama önizlemesi geliştiren bir geliştirici açısından pratik görev, bu yaşam süresini bilinçli biçimde seçmektir. Bu örnek kısa süre bekler, sentetik [4, 8, 12] değerlerini toplar ve toplamı biçimlendirir. Her şey yerel olarak bellekte çalışır.
Hesaplamanızın yaşam süresi nasıl olmalı?
Swift yakalama listeleri, bir kapatmanın neyi elde tutacağını belirtmenizi sağlar. Güçlü yakalama, başvurulan örneği elde tutar; zayıf başvuru ise bu örneğin serbest bırakılmasına izin verir ve sonrasında nil olur. Bunlar, hesaplamanın ne kadar beklediğinden bağımsız, belgelenmiş dil davranışlarıdır. Automatic Reference Counting
Yakalama sözdizimini seçmeden önce bir sözleşme belirleyin. Aşağıdaki karar tablosu, bu dil kurallarını gecikmeli bir masaüstü önizlemesine uygular:
| İstenen davranış | Yakalama stratejisi | Çağıran taraf sahibine başvuruyu bıraktıktan sonra ne olur? |
|---|---|---|
| Aynı sahibi kullanarak tamamla | [self] |
Görev, çalışması boyunca sahibin kullanılabilir kalmasını sağlar. |
| Yalnızca sahibi kullanılabilir durumdaysa devam et | [weak self] |
Görev, bekledikten sonra sahibin hâlâ var olup olmadığını denetler. |
| Zamanlama sırasında seçilen değerleri kullanarak tamamla | [samples = self.samples] |
Görev, bu sahibin ömrünü uzatmadan yakalanan değerleri elde tutar. |
Anlık görüntü satırında önemli bir koşul vardır: Bu örnek bir tamsayı dizisi yakalar. Yakalama listesi ifadeleri, kapatma oluşturulurken değerlendirilir. Bunun yerine bir sınıf örneği yakalamak o örneğe başvuruyu elde tutar; değiştirilebilir içeriğinin bağımsız bir kopyasını oluşturmaz. Expressions
Ayrıca ayrı bir değer sorusu da vardır. Aşağıdaki güçlü ve zayıf sürümler, gecikmeden sonra sahibin değerlerini okur. Anlık görüntü sürümü, zamanlama anındaki değerleri kullanır. Bekleme sırasında önizlemenin girdileri değişebiliyorsa sonucun hangi yorumu yansıtması gerektiğine karar verin.
Mac'inizde eksiksiz bir yerel karşılaştırma çalıştırın
macOS 13 veya daha yenisini ve kurulu Swift 6 veya daha yeni bir araç zincirini kullanın. Burada kullanılan süre tabanlı Task.sleep(for:) aşırı yükü macOS 13 ve sonrası için belgelenmiştir. Temel iş parçacığını engellemeden görevi askıya alır ve bekleme bitmeden iptal gerçekleşirse CancellationError fırlatır. sleep(for:tolerance:clock:)
Aşağıdaki tam programı CaptureDemo.swift adıyla kaydedin. Hesaplayıcı, görev kapatmaları ve gösterim @MainActor üzerinde çalışır; böylece durum erişimleri sıralanır. Swift, hem sınıf düzeyindeki ana aktör yalıtımını hem de burada kullanılan açık kapatma ek açıklamasını belgeler. 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)
}
}
Aritmetik, tamsayıları tek bir toplamda birleştirmek için standart kütüphanedeki reduce(0, +) işlevini kullanır. Foundation'daki NumberFormatter, bu toplamı görüntü metnine dönüştürür; string(from:) yöntemi isteğe bağlı bir dize döndürür, bu nedenle program biçimlendirme hatasını açıkça bildirir. Sabit örnek 24 toplamını verir. reduce(::), NumberFormatter, string(from:)
Terminal'de dosyanın bulunduğu klasöre geçin ve şu komutları çalıştırın. Hedef, Mac'inizin işlemci mimarisi için macOS 13'ü belirtir. -parse-as-library seçeneği, dosyanın açık @main giriş noktasına izin verir. Swift derleyicisi kaynak dosyaları ve komut satırı seçeneklerini kabul eder; swiftc -help, araç zincirinizle kurulu seçenekleri listeler. Swift command-line manual
swift --version
swiftc -parse-as-library -swift-version 6 -target "$(uname -m)-apple-macos13.0" CaptureDemo.swift -o CaptureDemo
./CaptureDemo
Beklenen uygulama çıktısı aşağıda gösterilmiştir. Bu çıktı, yakalama ve iptal kurallarından türetilmiştir; bu makalenin araştırma ortamında Swift derleyicisi bulunmadığından program incelendi ancak burada çalıştırılmadı.
strong retains owner: true
Total: 24
weak retains owner: false
Owner unavailable
snapshot retains owner: false
Total: 24
snapshot, cancelled retains owner: false
Cancelled
Zayıf probe, başka bir güçlü başvuru eklemeden sahipliği gözlemler. Her gösterim, görevi beklemeden önce kendi güçlü başvurusunu temizler. Gösterim ve görev kapatmaları aynı aktörü kullandığından, yeni oluşturulan kapatma bu eşzamanlı kurulum sırasında araya giremez. Böylece karşılaştırma, görevin ne kadar hızlı başladığını tahmin etmeye bağlı olmaz. Concurrency
Zayıf denetimin konumu neden önemli?
Zayıf dalda guard let self, gecikmeden sonra yer alır. Bu nedenle görev, sahibini güçlü biçimde elde tutmadan bekleyebilir. Sahip ortadan kalkmışsa açık Owner unavailable sonucu bu hesaplamayı sonlandırır.
Açmayı gecikmeden önceye taşımak yaşam süresini değiştirir. Aşağıdaki örnek, zayıf dalın kapatma gövdesinin yerine geçen açıklayıcı bir parçadır; ayrı bir program değildir:
// 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)
Burada başarılı isteğe bağlı bağlama, askıya alma sonrasında yeniden gereken güçlü bir yerel başvuru oluşturur. Dolayısıyla bu bağlama başarılı olduğunda, bekleme boyunca sahip kullanılabilir durumda kalır. Bu, Swift'in güçlü başvuru kurallarından kaynaklanır: zayıf yakalama, kapatmanın başlangıçta sahibin erişimini nasıl sağladığını belirlerken açılmış yerel değişken, sonraki kodun sahibini nasıl elde tuttuğunu belirler. Automatic Reference Counting
“Beklerken sahibini elde tutma, ardından yalnızca hâlâ varsa hesapla” sözleşmesi için açma işlemini bekleme sonrasına koyun ve küçük hesaplamayı hemen ardından yapın. Açma işleminden sonra ek askıya alma noktaları yeni bir yaşam süresi kararı gerektirir.
unowned yakalaması, başvurulan örneğin her erişim anında geçerli olmasını gerektirir. Bu örnekte ihtiyaç duyulan isteğe bağlı yokluk denetimini sağlamaz; çünkü gecikme sırasında sahibin serbest bırakılması istenmektedir. Automatic Reference Counting
Görev tanıtıcısını bırakmak hesaplamayı durdurur mu?
Tanıtıcısını bıraksanız bile görev çalışmaya devam eder. Tanıtıcıyı saklamak, sonucunu beklemenizi veya iptal isteğinde bulunmanızı sağlar. Apple ayrıca tamamlanmış bir görevin işlem kapatmasını ve bu kapatmanın tuttuğu başvuruları hemen serbest bıraktığını belgeler. Bu nedenle sınırlı süreli güçlü yakalama, kalıcı bir başvuru döngüsüne yol açmadan sahibin yaşam süresini iş boyunca uzatabilir. Task
İptal, sahiplikten ayrı bir sözleşmedir. Son gösterim, tanıtıcı üzerinden iptal isteğinde bulunur. Hata fırlatan sleep ve sonraki Task.checkCancellation(), işlemin durabileceği noktalar sağlar; çağıran taraf da bu sonucu bildirir. Swift'te iptal işbirliğine dayalıdır: iptal isteği, gelişigüzel hesaplama kodunu zorla kesintiye uğratmaz. Task
İptalin sonuç üretmeyi engellemesi gerekiyorsa, hata fırlatan sleep'i try? ile değiştirip koşulsuz olarak devam etmeyin. Bu hatayı bastırmak, başka bir iptal denetimi durdurmadığı sürece hesaplamaya ulaşılmasına izin verir. Benzer şekilde, sahibin değişkenine nil atamak bir iptal isteği değildir.
Bir masaüstü önizlemesinde bu ayrımlar üç yararlı denetim sağlar: sahibin bellekte tutulup tutulmayacağı, hesaplamanın hangi girdi değerlerini kullanacağı ve işlemin durdurulmasının istenip istenmediği. Zayıf yakalamanın bu üçünü birden yapmasını beklemek yerine her birini açıkça seçin.
Masaüstü önizlemesi için neyi değiştirmelisiniz?
Bekleyen bir önizleme yalnızca sahibi kullanılabilir durumdayken anlam taşıyorsa zayıf sürümü kullanın. Seçilen sentetik girdiler bağımsız olarak sonuç üretmeliyse anlık görüntü sürümünü kullanın. İşlemin tamamlanması için sahibin gerçekten gerekli olduğu durumlarda güçlü sürümü kullanın.
Nesne yaşam süresi ile arayüzün görünürlüğü de farklıdır. Bir pencerenin kapanması, hesaplayıcısına yönelik tüm güçlü başvuruların kaldırıldığını tek başına kanıtlamaz. Önizleme kapandığında uygulamanın bekleyen işleri durdurması gerekiyorsa, bu uygulama olayını saklanan görev tanıtıcısının cancel() yöntemine bağlayın; hesaplama yine de iptalle işbirliği yapmalıdır. Task
Bu örneğin iş yükünü küçük tutun. Ana aktör yalıtımı durum erişimini anlaşılır kılar ve bekleme askıya alınır; ancak eşzamanlı toplam yine de bu aktörde çalışır. Swift'in eşzamanlılık kılavuzu, ana aktörün arayüz işlerini de yürüttüğünü açıklar. Çok daha büyük bir hesaplama için uygun değerleri çalışan iş parçacığına aktaran ayrı bir yürütme tasarımı gerekir. Concurrency
Bu yerel hesaplama için belirleyici sınama basittir: gecikmeli işlem çalışmadan önce çağıran tarafın başvurusunu kaldırın ve sonucu gözlemleyin. Güçlü yakalama sahibini korur, zayıf yakalama açık bir kullanılamıyor sonucuna izin verir, tamsayı dizisinin anlık görüntüsü ise tamamlamak için gereken yalnızca girdileri korur.
Kaynaklar
- Automatic Reference Counting — Swift.org
- Expressions — Swift.org
- Concurrency — Swift.org
- Task — Apple Developer
- sleep(for:tolerance:clock:) — Apple Developer
- reduce(::) — Apple Developer
- NumberFormatter — Apple Developer
- string(from:) — Apple Developer
- Swift command-line manual — Swift project