Actor — adalah tipe yang diperkenalkan di Swift 5.5+ yang memecahkan masalah race condition data pada tingkat bahasa. Berbeda dengan kunci manual dan antrian DispatchQueue, actor secara otomatis mengisolasi keadaannya dan menyinkronkan akses ke sana. Ini berarti dua thread tidak dapat secara bersamaan mengubah properti yang sama dari tipe actor, yang menghilangkan race condition tanpa upaya tambahan dari pengembang. Mekanismenya didasarkan pada konsep isolasi actor, di mana kompiler melacak akses ke properti dan metode actor. Menurut Apple, 2025, model actor adalah bagian fundamental dari sistem konkurensi Swift.
Poin Utama
Actor — adalah tipe referensi, mirip dengan kelas, tetapi dengan perlindungan otomatis terhadap race condition. Ini diperkenalkan di Swift 5.5 sebagai bagian dari sistem konkurensi bersama dengan async/await dan Task. Actor menjamin bahwa keadaannya yang dapat berubah tidak akan pernah dibaca atau ditulis secara bersamaan dari thread yang berbeda tanpa sinkronisasi eksplisit.
Untuk mendeklarasikan actor, gunakan kata kunci actor, diikuti oleh kurung kurawal dengan anggotanya. Secara sintaksis, actor mirip dengan kelas, tetapi perilakunya sangat berbeda.
actor BankAccount {
private var balance: Double
init(initialBalance: Double) {
self.balance = initialBalance
}
func deposit(amount: Double) {
balance += amount
}
func getBalance() -> Double {
return balance
}
}
Kompiler secara otomatis mengisolasi semua properti dan metode actor sehingga hanya dapat diakses di dalam konteks actor. Mencoba mengakses balance dari luar actor akan menghasilkan kesalahan kompiler jika panggilan tidak ditandai sebagai async.
Isolasi data — adalah konsep kunci dari actor. Actor menjamin akses saling eksklusif ke keadaannya melalui mekanisme eksekutor actor (actor executor). Setiap actor memiliki eksekutornya sendiri yang memproses semua akses ke anggota yang diisolasi secara sekuensial.
Ketika kode di luar actor memanggil metodenya, panggilan ditempatkan dalam antrian eksekutor actor. Actor hanya menjalankan satu tugas pada satu waktu, yang menjamin tidak adanya race condition. Jika dua thread secara bersamaan memanggil deposit, panggilan kedua menunggu yang pertama selesai.
let account = BankAccount(initialBalance: 1000.0)
// Panggilan asinkron — diperlukan dari luar actor
await account.deposit(amount: 500.0)
let currentBalance = await account.getBalance()
Setiap panggilan metode actor memerlukan await, karena actor mungkin sedang sibuk dengan tugas lain. Ini bukan bug, tetapi desain sadar yang mencegah race condition. Swift secara implisit membuat getter dan setter properti actor menjadi async, sehingga membaca properti juga memerlukan await.
Actor dan kelas adalah tipe referensi, tetapi perilaku mereka di lingkungan multi-threaded sangat berbeda. Kelas tidak memberikan perlindungan otomatis terhadap race condition, sementara actor membangunnya di tingkat kompiler melalui sistem tipe.
| Karakteristik | Actor | Class |
|---|---|---|
| Perlindungan race | Otomatis, di tingkat kompiler | Memerlukan sinkronisasi manual |
| Akses ke properti | Hanya melalui await dari luar | Langsung, tanpa sinkronisasi |
| Pewarisan | Hanya dari actor lain | Pewarisan kelas standar |
| Kesesuaian protokol | Dapat mematuhi protokol | Standar |
| Kinerja | Overhead rendah saat isolasi | Tanpa sinkronisasi — lebih cepat |
Actor hanya dapat mewarisi dari actor lain dan tidak dapat mewarisi dari kelas. Ini dilakukan secara sadar, karena kelas tidak memiliki mekanisme isolasi actor, dan mencampur dua model akan menyebabkan perilaku tidak terdefinisi.
actor SavingsAccount: BankAccount {
func applyInterest(rate: Double) {
let interest = balance * rate
balance += interest
}
}
Panggilan asinkron — adalah mekanisme interaksi dengan actor dari kode eksternal. Karena actor mengisolasi keadaannya, setiap akses ke anggotanya dari luar memerlukan await. Ini memungkinkan Swift menjamin bahwa kode pemanggil tidak memblokir thread dan actor dapat memproses permintaan lain.
Selain tipe actor yang dideklarasikan, Swift mendukung actor global — atribut @MainActor, yang menandai kelas, properti, atau metode sebagai dijalankan di thread utama. Ini sangat berguna saat bekerja dengan UIKit dan SwiftUI.
@MainActor
class ViewModel: ObservableObject {
@Published var title: String = ""
func updateTitle() {
// Kode ini dijamin berjalan di thread utama
title = "New Title"
}
}
Penggunaan @MainActor menghilangkan kebutuhan untuk memanggil DispatchQueue.main.async secara manual, yang membuat kode lebih bersih dan aman. Kompiler memeriksa bahwa perpindahan ke thread utama terjadi dengan benar.
Nonisolated — adalah kata kunci yang memungkinkan menandai metode atau properti terhitung dari actor sebagai tidak terisolasi. Anggota tersebut tidak memiliki akses ke keadaan terisolasi actor, tetapi dapat dipanggil tanpa await dari luar actor.
Metode nonisolated berguna untuk perhitungan yang tidak bergantung pada keadaan actor yang dapat berubah. Misalnya, metode formatBalance tidak mengakses balance secara langsung, hanya memformat nilai yang dilewatkan — metode seperti itu dapat dengan aman dibuat nonisolated.
actor BankAccount {
private var balance: Double = 0
nonisolated func formatBalance(amount: Double) -> String {
return "$\(amount)"
}
}
Anggota nonisolated dijalankan secara sinkron dan tidak memerlukan await. Namun, mereka tidak dapat langsung membaca properti terisolasi actor. Jika metode nonisolated memerlukan nilai dari actor, itu harus dilewatkan sebagai parameter.
Reentrancy — adalah mekanisme yang memungkinkan masuk kembali ke actor selama menunggu panggilan asinkron. Tanpa reentrancy, actor dapat selamanya terblokir jika satu metodenya menunggu metode lain, yang pada gilirannya menunggu yang pertama.
Ketika kode di dalam actor menjalankan await, actor menangguhkan tugas saat ini dan dapat memproses tugas lain yang ditempatkan dalam antrian. Setelah await selesai, tugas dilanjutkan. Ini mencegah deadlock, tetapi memerlukan kehati-hatian: keadaan actor di antara titik await dapat berubah.
actor DataProcessor {
var cache: [Int: String] = [:]
func process(id: Int) async -> String {
if let cached = cache[id] {
return cached
}
// await — titik masuk kembali
let result = await fetchData(id: id)
// Cache mungkin berubah setelah await — periksa lagi
cache[id] = result
return result
}
}
Pengembang harus mempertimbangkan reentrancy dan memeriksa keadaan actor setelah titik await. Kesalahan umum adalah asumsi bahwa isolasi actor dipertahankan melalui jeda asinkron. Dalam praktiknya, antara await dan instruksi berikutnya, keadaan dapat berbeda dari yang diharapkan.
Pertanyaan yang Sering Diajukan
Actor secara otomatis mengisolasi keadaannya dari race condition, memerlukan await untuk akses dari luar. Kelas tidak memberikan perlindungan seperti itu — pengembang sendiri yang bertanggung jawab atas sinkronisasi melalui kunci atau antrian. Actor hanya mewarisi dari actor, kelas dari kelas.
Ya, actor dapat mewarisi dari actor lain. Subkelas menerima semua properti dan metode terisolasi dari induk. Actor tidak dapat mewarisi dari kelas, karena kelas tidak memiliki mekanisme isolasi actor di tingkat kompiler.
Konteks actor-isolated — adalah area kode di mana akses langsung ke keadaan actor yang dapat berubah diizinkan. Di dalam metode actor yang ditandai sebagai isolated (default), properti dapat dibaca dan ditulis tanpa await. Kompiler memeriksa batas isolasi.
Data dari actor ditransfer melalui metode async yang mengembalikan tipe Sendable, atau melalui metode nonisolated yang menerima nilai sebagai parameter. Juga dapat membuat properti async yang mengembalikan snapshot keadaan actor dalam bentuk struktur Sendable.
Ya, actor dapat mematuhi protokol. Jika protokol berisi persyaratan terisolasi (actor-isolated), mereka secara otomatis menjadi actor-terisolasi. Untuk metode asinkron dalam protokol, dapat ditentukan dengan marker isolated bahwa mereka harus dipanggil pada actor tertentu.
Kesimpulan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga