@MainActor — apa itu, penerapan dan fitur dalam kode asinkron Swift

Penulis: IT Sectr Diterbitkan: 2026-03-19 Waktu membaca: 8 mnt

@MainActor adalah aktor global dalam bahasa Swift yang menjamin eksekusi kode pada thread utama. Menurut Apple Developer, 2024, @MainActor mengotomatiskan perpindahan ke thread utama saat bekerja dengan UI, membebaskan pengembang dari panggilan manual DispatchQueue.main.async. Anotasi muncul di Swift 5.5 bersama dengan sistem async/await.

Poin utama

  • @MainActor — aktor global Swift untuk menjamin eksekusi pada thread utama.
  • Sistem async/await — dasar kerja @MainActor.
  • Anotasi kelas secara otomatis menempatkan semua metodenya pada thread utama.
  • Berbeda dengan DispatchQueue.main, @MainActor memeriksa thread di level compiler.
  • Pembaruan UI — area utama penerapan @MainActor dalam pengembangan iOS.

Apa itu @MainActor?

@MainActor adalah aktor global (global actor) di Swift yang menggabungkan properti aktor dengan jaminan eksekusi pada thread utama aplikasi. Ini adalah bagian dari sistem konkurensi Swift, yang diperkenalkan di Swift 5.5 bersama async/await dan konkurensi terstruktur. Anotasi memungkinkan pengembang untuk tidak memikirkan perpindahan thread manual dan mengurangi jumlah kesalahan UI.

Definisi dan tempat di Swift Concurrency

Aktor di Swift adalah tipe referensi yang mengisolasi statusnya dan menjamin bahwa hanya satu thread yang dapat mengubahnya. @MainActor adalah aktor global khusus yang eksekutornya adalah thread utama. Kode apa pun yang ditandai dengan @MainActor dieksekusi pada thread utama — bahkan jika dipanggil dari tugas latar belakang.

Sebelum munculnya @MainActor, pengembang beralih secara manual ke thread utama melalui DispatchQueue.main.async. Ini adalah sumber kesalahan yang sering terjadi: pengembang lupa beralih, yang menyebabkan crash karena pembaruan UI di thread non-utama. @MainActor memecahkan masalah ini di level sistem tipe.

Alasan penciptaan

Sumber sebagian besar bug di aplikasi iOS adalah ketidakamanan UI — memperbarui antarmuka dari thread latar belakang. Apple menyematkan @MainActor ke dalam Swift Concurrency untuk membuat perpindahan ke thread utama otomatis dan dapat diperiksa oleh compiler, menghilangkan seluruh kelas kesalahan runtime.

Bagaimana cara kerja @MainActor?

Prinsip kerja @MainActor didasarkan pada sistem eksekusi Swift Concurrency. Ketika sebuah thread memanggil fungsi yang ditandai dengan @MainActor, penjadwal menangguhkannya pada eksekutor saat ini dan melanjutkannya pada thread utama. Compiler melacak batas panggilan dan menjamin keamanan.

Eksekutor thread utama

Untuk eksekusi @MainActor bertanggung jawab MainActor.shared — eksekutor yang terkait dengan thread utama aplikasi. Ketika fungsi asinkron ditandai dengan @MainActor, fungsi tersebut selalu dilanjutkan pada eksekutor ini, terlepas dari thread mana tugas asli dimulai.

swift
import SwiftUI

class ViewModel: ObservableObject {
    @Published var items: [String] = []

    @MainActor
    func loadData() async {
        let result = await fetchRemoteData()
        items = result  // aman, MainActor menjamin thread utama
    }
}

Warisan konteks aktor

Jika sebuah fungsi ditandai dengan @MainActor dan memanggil fungsi asinkron lain, secara default ia mewarisi konteks aktor. Ini berarti semua panggilan bersarang juga dieksekusi pada thread utama, kecuali ditentukan lain. Compiler melacak ini dan memberikan kesalahan saat mencoba mengirimkan closure yang tidak sesuai.

@MainActor vs DispatchQueue.main

Perbandingan @MainActor dan DispatchQueue.main membantu memahami mengapa mekanisme baru dianggap lebih aman dan nyaman, meskipun keduanya menyelesaikan tugas yang sama — eksekusi kode pada thread utama.

Keamanan di level tipe

@MainActor adalah pemeriksaan di level compiler. Jika Anda mencoba memanggil fungsi @MainActor dari konteks yang tidak aman, compiler akan memberikan peringatan atau kesalahan. DispatchQueue.main.async adalah panggilan runtime: kode akan dikompilasi, tetapi dapat crash di runtime saat mencoba memperbarui UI dari thread latar belakang.

Kinerja dan redundansi

DispatchQueue.main.async menambahkan blok ke antrian yang dapat dieksekusi dengan penundaan. @MainActor dengan async/await melakukan perpindahan langsung eksekutor tanpa membuat closure tambahan. Ini mengurangi overhead dan membuat kode lebih dapat diprediksi dalam hal waktu eksekusi.

swift
// Pendekatan lama
DispatchQueue.main.async {
    self.updateUI()
}

// Pendekatan baru dengan @MainActor
@MainActor
func updateUI() {
    // dieksekusi pada thread utama
    self.label.text = "Diperbarui"
}
Kriteria@MainActorDispatchQueue.main
Pemeriksaancompilerruntime
Sintaksanotasi (deklaratif)panggilan (imperatif)
Overheadrendah (perpindahan eksekutor)sedang (closure + antrian)
Kemampuan ujitinggi (MainActor.shared bisa diganti)rendah (sulit di-mock)

Penggunaan @MainActor di proyek iOS

Dalam proyek iOS nyata, @MainActor diterapkan di lapisan ViewModel, tampilan SwiftUI dan controller UIKit. Anotasi dapat diterapkan baik pada metode individu maupun seluruh tipe.

Anotasi kelas atau struktur

Dengan menandai kelas dengan @MainActor, Anda menjamin bahwa semua metode dan propertinya hanya dapat diakses pada thread utama. Ini sangat nyaman untuk tampilan SwiftUI dan kelas ObservableObject: Anda cukup menambahkan @MainActor sebelum class, dan semua properti @Published diperbarui dengan aman.

swift
@MainActor
final class UserListViewModel: ObservableObject {
    @Published var users: [User] = []
    @Published var isLoading = false

    func fetchUsers() async {
        isLoading = true
        users = await api.getUsers()
        isLoading = false
    }
}

Membungkus kode lama

Saat bekerja dengan kode UIKit lama, di mana perpindahan thread dilakukan secara manual, Anda dapat menggunakan MainActor.run untuk perpindahan eksplisit. Ini nyaman untuk transisi bertahap ke Swift Concurrency tanpa menulis ulang seluruh basis kode.

swift
await MainActor.run {
    self.tableView.reloadData()
}

Keterbatasan @MainActor

Terlepas dari semua kelebihannya, @MainActor memiliki sejumlah keterbatasan yang harus dipertimbangkan saat merancang arsitektur aplikasi. Memahami batas penerapan membantu menghindari penggunaan yang salah.

Kinerja saat penggunaan intensif

Jika seluruh rantai panggilan ditandai dengan @MainActor, setiap pekerjaan berat akan dieksekusi pada thread utama, menyebabkan pembekuan UI. Disarankan untuk menandai hanya lapisan UI dengan @MainActor, dan membiarkan logika bisnis serta permintaan jaringan di aktor latar belakang atau eksekutor global.

Ketidakcocokan dengan beberapa API

API lama berbasis callback (misalnya URLSession tanpa async/await) tidak mendukung konteks aktor. Untuk integrasi, diperlukan pembungkus dengan CheckedContinuation. Juga, @MainActor tidak kompatibel dengan performSelector, target-action, dan pola UIKit non-asinkron lainnya.

Debugging multi-thread

Saat melakukan debugging aplikasi dengan @MainActor, lebih sulit untuk mereproduksi kondisi balapan, karena compiler mencegah banyak dari mereka pada fase pembangunan, bukan di runtime. Namun, ini dapat menciptakan rasa aman yang palsu: bekerja secara tidak benar dengan objek mutable bersama (misalnya NSCache atau variabel global bersama) masih mungkin terjadi jika tidak ditandai dengan @MainActor dan digunakan tanpa sinkronisasi eksplisit.

Pengujian @MainActor

@MainActor secara signifikan menyederhanakan pengujian logika UI, karena menghilangkan kebutuhan untuk beralih thread secara manual dalam pengujian. Namun, ada fitur yang perlu dipertimbangkan saat menulis pengujian unit dan pengujian UI.

Pengujian unit dengan MainActor

Di XCTest, lingkungan eksekusi secara otomatis mengkonfigurasi eksekutor thread utama. Ketika metode pengujian dijalankan pada thread utama, pemanggilan fungsi @MainActor tidak memerlukan pengaturan tambahan — mereka dieksekusi dalam konteks yang sama. Untuk menguji skenario latar belakang, gunakan MainActor.run di dalam Task dengan menentukan prioritas dan eksekutor secara eksplisit, periksa secara terpisah bahwa kode berfungsi dengan benar saat dipanggil dari latar belakang.

Salah satu pendekatan umum adalah menguji ViewModel dengan @MainActor, di mana diperiksa bahwa properti @Published diperbarui dengan benar setelah operasi asinkron. Berkat warisan konteks aktor, panggilan await di dalam pengujian menjamin eksekusi pada thread utama tanpa jaminan DispatchQueue tambahan dan perpindahan konteks manual, yang menyederhanakan penulisan pengujian.

Memeriksa isolasi saat refactoring

Saat merefaktor kode yang ada ke Swift Concurrency, periksa isolasi @MainActor melalui compiler: setiap panggilan metode sinkron tanpa @MainActor dari konteks @MainActor ditandai sebagai kesalahan. Properti ini digunakan untuk transisi bertahap proyek ke async/await: Anda menandai lapisan ViewModel sebagai @MainActor, dan compiler menyoroti semua panggilan tidak aman yang perlu dipindahkan ke aktor latar belakang.

Mocking dan konteks aktor

Saat membuat mock untuk dependensi @MainActor, gunakan protokol dengan metode async yang mendeklarasikan fungsi asinkron dengan tipe pengembalian. Ini memungkinkan penggantian layanan jaringan, database, dan dependensi eksternal lainnya tanpa melanggar isolasi aktor. Compiler akan memeriksa bahwa mock memenuhi semua persyaratan isolasi, mencegah akses tidak sengaja ke kode @MainActor dari thread pengujian latar belakang.

Menunggu operasi asinkron

Saat menguji kode @MainActor secara sinkron, gunakan XCTestExpectation untuk menunggu penyelesaian operasi asinkron. Atur ekspektasi dalam pengujian dan lakukan fulfillment di dalam closure yang dieksekusi pada thread utama. Jika pengujian menggantung tanpa batas — kemungkinan besar panggilan pada thread utama tidak terjadi dan isolasi aktor perlu diperiksa. Untuk debugging konteks eksekusi, berguna untuk menambahkan pemeriksaan Thread.isMainThread di dalam kode pengujian.

Pertanyaan yang sering diajukan

Apakah wajib menandai seluruh kelas dengan @MainActor?

Tidak, cukup menandai hanya metode yang memperbarui UI. Namun, jika ada beberapa metode seperti itu di kelas, lebih mudah menambahkan @MainActor ke seluruh kelas. Ini menjamin bahwa semua anggotanya dieksekusi pada thread utama dan menyederhanakan pemeliharaan kode.

Apa perbedaan @MainActor dengan @globalActor?

@MainActor adalah instance konkret dari aktor global, terikat ke thread utama. @globalActor adalah protokol untuk membuat aktor global Anda sendiri. Misalnya, Anda dapat membuat @BackgroundActor untuk mengeksekusi kode pada thread latar belakang, jika arsitektur proyek membutuhkannya.

Bisakah @MainActor digunakan tanpa async/await?

Ya, fungsi sinkron dengan @MainActor juga dieksekusi pada thread utama. Namun, nilai utama @MainActor terungkap tepat dengan async/await, ketika fungsi asinkron secara otomatis dilanjutkan pada thread utama tanpa perpindahan manual melalui DispatchQueue.main.

Bagaimana cara membatalkan tugas @MainActor?

Task.cancel() bekerja dengan tugas @MainActor sama seperti tugas biasa. Tugas @MainActor dapat memeriksa Task.isCancelled atau melempar CancellationError. Saat pembatalan, thread utama tidak diblokir — tugas hanya berhenti dieksekusi pada titik penangguhan terdekat.

Apa yang terjadi jika @MainActor dipanggil dari thread latar belakang?

Compiler menjamin keamanan: jika Anda memanggil fungsi @MainActor dari konteks latar belakang, compiler akan menunjukkan kesalahan. Untuk panggilan asinkron, cukup menandai kode pemanggil dengan await, dan eksekutor akan beralih sendiri ke thread utama. Untuk panggilan sinkron, diperlukan perpindahan eksplisit melalui MainActor.run.

Ringkasan

  • @MainActor — aktor global Swift yang menjamin eksekusi pada thread utama.
  • Pemeriksaan oleh compiler menghilangkan seluruh kelas kesalahan keamanan UI.
  • Anotasi seluruh kelas secara otomatis menempatkan semua metodenya pada thread utama.
  • MainActor.run — perpindahan eksplisit untuk kode lama dan konteks sinkron.
  • Berbeda dengan DispatchQueue.main, @MainActor tidak membuat closure dan menggunakan perpindahan eksekutor.
  • Perhitungan berat tidak boleh dieksekusi di bawah @MainActor untuk menghindari pembekuan UI.
  • Warisan konteks aktor menyederhanakan rantai panggilan asinkron dan membuat kode sekuensial, dapat diprediksi, dan aman untuk UI.

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.

Diskusikan proyek

Baca juga