@MainActor, Swift dilinde ana iş parçacığında kod yürütülmesini garanti eden küresel bir aktördür. Apple Developer, 2024'e göre, @MainActor, UI ile çalışırken ana iş parçacığına geçişi otomatikleştirerek geliştiricinin manuel olarak DispatchQueue.main.async çağrısı yapmasını ortadan kaldırır. Ek açıklama, Swift 5.5'te async/await sistemiyle birlikte ortaya çıktı.
Önemli Noktalar
@MainActor, Swift'te aktörlerin özelliklerini uygulamanın ana iş parçacığında yürütme garantisiyle birleştiren küresel bir aktördür. Swift 5.5'te async/await ve yapılandırılmış eşzamanlılık ile birlikte tanıtılan Swift eşzamanlılık sisteminin bir parçasıdır. Ek açıklama, geliştiricinin manuel iş parçacığı geçişi hakkında düşünmesine gerek bırakmaz ve UI hatalarının sayısını azaltır.
Swift'te aktör, durumunu yalıtan ve yalnızca bir iş parçacığının onu değiştirebilmesini garanti eden bir referans türüdür. @MainActor, yürütücüsü ana iş parçacığı olan özel bir küresel aktördür. @MainActor ile işaretlenmiş herhangi bir kod, arka plan görevinden çağrılmış olsa bile ana iş parçacığında yürütülür.
@MainActor'dan önce, geliştiriciler DispatchQueue.main.async aracılığıyla manuel olarak ana iş parçacığına geçiş yapıyordu. Bu, sık hataların kaynağıydı: geliştiriciler geçiş yapmayı unutuyor ve ana iş parçacığı dışında UI güncellemesi nedeniyle çökmelere yol açıyordu. @MainActor bu sorunu tür sistemi seviyesinde çözer.
iOS uygulamalarındaki hataların çoğunun kaynağı UI güvensizliğidir — arayüzün arka plan iş parçacığından güncellenmesi. Apple, @MainActor'ı Swift Concurrency'e dahil ederek ana iş parçacığına geçişi otomatik ve derleyici tarafından kontrol edilebilir hale getirdi ve böylece bir runtime hata sınıfını tamamen ortadan kaldırdı.
@MainActor'ın çalışma prensibi Swift Concurrency yürütme sistemine dayanır. Bir iş parçacığı @MainActor ile işaretlenmiş bir işlevi çağırdığında, zamanlayıcı onu mevcut yürütücüde askıya alır ve ana iş parçacığında devam ettirir. Derleyici, çağrı sınırlarını izler ve güvenliği garanti eder.
@MainActor'ın yürütülmesi, uygulamanın ana iş parçacığıyla ilişkili bir yürütücü olan MainActor.shared tarafından yönetilir. Asenkron bir işlev @MainActor ile işaretlendiğinde, orijinal görevin hangi iş parçacığında başlatıldığına bakılmaksızın her zaman bu yürütücüde devam eder.
import SwiftUI
class ViewModel: ObservableObject {
@Published var items: [String] = []
@MainActor
func loadData() async {
let result = await fetchRemoteData()
items = result // güvenle, MainActor ana iş parçacığını garanti eder
}
}
Bir işlev @MainActor ile işaretlenmişse ve başka bir asenkron işlevi çağırıyorsa, varsayılan olarak aktör bağlamını miras alır. Bu, aksi belirtilmedikçe tüm iç içe çağrıların da ana iş parçacığında yürütüldüğü anlamına gelir. Derleyici bunu izler ve tutarsız bir closure geçirmeye çalışıldığında hata verir.
@MainActor ve DispatchQueue.main karşılaştırması, her ikisi de aynı görevi — ana iş parçacığında kod yürütmeyi — çözmesine rağmen, yeni mekanizmanın neden daha güvenli ve kullanışlı kabul edildiğini anlamaya yardımcı olur.
@MainActor, derleyici seviyesinde bir kontroldür. Güvenli olmayan bir bağlamdan @MainActor işlevini çağırmaya çalışırsanız, derleyici bir uyarı veya hata verecektir. DispatchQueue.main.async bir runtime çağrısıdır: kod derlenecek ancak arka plan iş parçacığından UI güncellemeye çalışıldığında runtime'da çökebilir.
DispatchQueue.main.async, kuyruğa gecikmeyle yürütülebilecek bir blok ekler. @MainActor async/await ile gereksiz closure'lar oluşturmadan doğrudan yürütücü geçişi yapar. Bu, ek yükü azaltır ve yürütme süresini daha öngörülebilir hale getirir.
// Eski yaklaşım
DispatchQueue.main.async {
self.updateUI()
}
// @MainActor ile yeni yaklaşım
@MainActor
func updateUI() {
// ana iş parçacığında yürütülür
self.label.text = "Güncellendi"
}
| Kriter | @MainActor | DispatchQueue.main |
|---|---|---|
| Kontrol | derleyici | runtime |
| Sözdizimi | ek açıklama (bildirimsel) | çağrı (emredici) |
| Ek yük | düşük (yürütücü geçişi) | orta (closure + kuyruk) |
| Test edilebilirlik | yüksek (MainActor.shared değiştirilebilir) | düşük (mock'laması zor) |
Gerçek iOS projelerinde, @MainActor ViewModel katmanlarında, SwiftUI görünümlerinde ve UIKit denetleyicilerinde kullanılır. Ek açıklama hem tek tek metotlara hem de tüm tipe uygulanabilir.
Bir sınıfı @MainActor ile işaretleyerek, tüm metotlarının ve özelliklerinin yalnızca ana iş parçacığında erişilebilir olmasını garanti edersiniz. Bu, SwiftUI görünümleri ve ObservableObject sınıfları için özellikle kullanışlıdır: sınıfın önüne @MainActor eklemeniz yeterlidir ve tüm @Published özellikleri güvenle güncellenir.
@MainActor
final class UserListViewModel: ObservableObject {
@Published var users: [User] = []
@Published var isLoading = false
func fetchUsers() async {
isLoading = true
users = await api.getUsers()
isLoading = false
}
}
İş parçacığı geçişinin manuel olduğu eski UIKit koduyla çalışırken, açık geçiş için MainActor.run kullanabilirsiniz. Bu, tüm kod tabanını yeniden yazmadan Swift Concurrency'e aşamalı geçiş için kullanışlıdır.
await MainActor.run {
self.tableView.reloadData()
}
Tüm avantajlarına rağmen, @MainActor'ın uygulama mimarisi tasarlanırken dikkate alınması gereken bir dizi sınırlaması vardır. Uygulanabilirlik sınırlarını anlamak, yanlış kullanımı önlemeye yardımcı olur.
Tüm çağrı zinciri @MainActor ile işaretlenmişse, herhangi bir ağır iş ana iş parçacığında gerçekleştirilir ve UI donmalarına neden olur. Yalnızca UI katmanının @MainActor ile işaretlenmesi, iş mantığı ve ağ isteklerinin arka plan aktörlerinde veya küresel yürütücüde bırakılması önerilir.
Eski callback tabanlı API'ler (örneğin, async/await olmadan URLSession) aktör bağlamını desteklemez. Entegrasyon için CheckedContinuation ile bir sarmalayıcı gereklidir. Ayrıca @MainActor, performSelector, target-action ve UIKit'in diğer asenkron olmayan desenleriyle uyumlu değildir.
@MainActor ile uygulamalarda hata ayıklarken, derleyici runtime yerine derleme zamanında birçoğunu önlediği için yarış koşullarını yeniden oluşturmak daha zordur. Ancak bu, yanlış bir güvenlik hissi yaratabilir: paylaşılan değişebilir nesnelerle (örneğin NSCache veya paylaşılan küresel değişkenler) yanlış çalışma, @MainActor ile işaretlenmemişlerse ve açık senkronizasyon olmadan kullanılıyorlarsa hala mümkündür.
@MainActor, testlerde manuel iş parçacığı değiştirme ihtiyacını ortadan kaldırdığı için UI mantığı testini önemli ölçüde basitleştirir. Ancak, birim testleri ve UI testleri yazarken dikkate alınması gereken özellikler vardır.
XCTest'te, test ortamı otomatik olarak ana iş parçacığı yürütücüsünü ayarlar. Bir test metodu ana iş parçacığında çalıştığında, @MainActor işlevlerini çağırmak ek kurulum gerektirmez — aynı bağlamda yürütülürler. Arka plan senaryolarını test etmek için, açık öncelik ve yürütücü ile Task içinde MainActor.run kullanın ve kodun arka plandan çağrıldığında doğru çalıştığını ayrıca doğrulayın.
Yaygın bir yaklaşım, asenkron işlemlerden sonra @Published özelliklerinin doğru şekilde güncellendiğini doğrulayan @MainActor ile ViewModel testidir. Aktör bağlam mirası sayesinde, test içinde await çağırmak, ek DispatchQueue garantileri veya manuel bağlam değiştirme olmadan ana iş parçacığında yürütmeyi garanti eder ve bu da test yazmayı basitleştirir.
Mevcut kodu Swift Concurrency'e yeniden düzenlerken, derleyici aracılığıyla @MainActor izolasyonunu kontrol edin: @MainActor bağlamından @MainActor olmayan senkron metotlara yapılan herhangi bir çağrı hata olarak işaretlenir. Bu özellik, bir projeyi kademeli olarak async/await'e taşımak için kullanılır: ViewModel katmanını @MainActor olarak işaretlersiniz ve derleyici, arka plan aktörlerine taşınması gereken tüm güvensiz çağrıları vurgular.
@MainActor bağımlılıkları için mock'lar oluştururken, dönüş türleriyle asenkron işlevler bildiren async metotlu protokoller kullanın. Bu, aktör izolasyonunu bozmadan ağ hizmetlerini, veritabanlarını ve diğer harici bağımlılıkları değiştirmeye olanak tanır. Derleyici, mock'ın tüm izolasyon gereksinimlerini uyguladığını doğrulayarak arka plan test iş parçacıklarından @MainActor koduna yanlışlıkla erişimi önler.
@MainActor kodunu senkron olarak test ederken, asenkron işlemlerin tamamlanmasını beklemek için XCTestExpectation kullanın. Testte beklentiyi ayarlayın ve ana iş parçacığında yürütülen bir closure içinde fulfillment'ı çağırın. Test süresiz olarak takılırsa — muhtemelen ana iş parçacığında çağrı gerçekleşmiyordur ve aktör izolasyonunu kontrol etmeniz gerekir. Yürütme bağlamında hata ayıklamak için, test kodu içinde Thread.isMainThread kontrolü eklemek faydalıdır.
Sıkça Sorulan Sorular
Hayır, yalnızca UI'yi güncelleyen metotları işaretlemek yeterlidir. Ancak, bir sınıfın bu tür birden çok metodu varsa, tüm sınıfa @MainActor eklemek daha basittir. Bu, tüm üyelerinin ana iş parçacığında yürütülmesini garanti eder ve kod bakımını basitleştirir.
@MainActor, ana iş parçacığına bağlı küresel bir aktörün belirli bir örneğidir. @globalActor, kendi küresel aktörlerinizi oluşturmak için bir protokoldür. Örneğin, proje mimarisi gerektiriyorsa, arka plan iş parçacığında kod yürütmek için @BackgroundActor oluşturabilirsiniz.
Evet, @MainActor ile senkron işlevler de ana iş parçacığında yürütülür. Ancak, @MainActor'ın asıl değeri async/await ile ortaya çıkar; asenkron bir işlev, DispatchQueue.main aracılığıyla manuel geçiş olmadan otomatik olarak ana iş parçacığında devam eder.
Task.cancel(), @MainActor görevleriyle normal görevlerle aynı şekilde çalışır. Bir @MainActor görevi Task.isCancelled'ı kontrol edebilir veya CancellationError fırlatabilir. İptal sırasında ana iş parçacığı bloke olmaz — görev, en yakın askıya alma noktasında yürütmeyi durdurur.
Derleyici güvenliği garanti eder: bir arka plan bağlamından @MainActor işlevini çağırırsanız, derleyici hatayı işaret edecektir. Asenkron çağrılar için, çağıran kodu await ile işaretlemek yeterlidir ve yürütücü ana iş parçacığına geçecektir. Senkron çağrılar için, MainActor.run aracılığıyla açık geçiş gereklidir.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun