- Published on
Swiftda @MainActor
- Authors
- Name
- ShoxruxC
- @iOSdasturchi
@MainActor — UI thread xavfsizligi
iOS da UI faqat main thread (asosiy oqim) da yangilanishi kerak. Bu Apple ning qat'iy qoidasi. Background thread dan UI ga tegish — ekranning qotib qolishi, noto'g'ri ko'rinish yoki ilova crash bo'lishiga olib keladi. @MainActor — bu qoidani kompilyatsiya vaqtida kafolatlaydi. Agar main thread da bo'lishi kerak bo'lgan kodni background dan chaqirsangiz — compiler xato beradi.
MainActor — Apple tomonidan yaratilgan global actor. U butun dasturda bitta va main (UI) thread da ishlaydi. @MainActor ni class, funksiya yoki property ga qo'yish mumkin.
Muammo — background dan UI yangilash
Quyida ikkita misol bor. Birinchisida @Published property background thread dan o'zgartiriladi — bu xavfli. Ikkinchisida @MainActor qo'yilgan — barcha UI o'zgarishlar avtomatik main thread da bajariladi.
// ═══════════════════════════════════════
// ❌ XATO — background thread dan UI yangilash
// ═══════════════════════════════════════
class XavfliViewModel: ObservableObject {
@Published var maqolalar: [String] = []
func yuklash() {
Task {
// Bu kod background thread da ishlashi mumkin
let data = try await URLSession.shared.data(
from: URL(string: "https://api.example.com")!
)
// ❌ @Published ni background dan o'zgartirish
// Swift 6 da kompilyatsiya xatosi!
self.maqolalar = ["Yangi maqola"]
}
}
}
// ═══════════════════════════════════════
// ✅ TO'G'RI — @MainActor bilan
// ═══════════════════════════════════════
@MainActor // Butun class main thread da
class XavfsizViewModel: ObservableObject {
@Published var maqolalar: [String] = []
@Published var yuklanmoqda = false
func yuklash() async {
yuklanmoqda = true // ✅ Main thread — xavfsiz
// await — background ga o'tadi, natija main ga qaytadi
do {
let (data, _) = try await URLSession.shared.data(
from: URL(string: "https://api.example.com")!
)
// ✅ Main thread ga qaytdi — xavfsiz
maqolalar = try JSONDecoder().decode([String].self, from: data)
} catch {
print("Xato: \(error)")
}
yuklanmoqda = false // ✅ Main thread — xavfsiz
}
}
@MainActor qo'llash usullari
@MainActor ni uch xil tarzda qo'llash mumkin: butun class ga (barcha property va metodlar main thread da), bitta funksiya ga (faqat shu funksiya main thread da, qolganlari erkin) yoki bitta property ga (faqat shu property main thread da o'zgartiriladi).
// ═══════════════════════════════════════
// 1. BUTUN CLASS GA
// ═══════════════════════════════════════
@MainActor
class ProfilViewModel: ObservableObject {
@Published var ism = "" // main thread da
@Published var rasm: UIImage? // main thread da
func yangilash() async { // main thread da
ism = "Ali"
}
}
// ═══════════════════════════════════════
// 2. FAQAT BITTA FUNKSIYAGA
// ═══════════════════════════════════════
class TarmoqXizmati {
// Bu funksiya background da ishlaydi
func yuklash() async throws -> [String] {
let (data, _) = try await URLSession.shared.data(
from: URL(string: "https://api.example.com")!
)
return try JSONDecoder().decode([String].self, from: data)
}
// Bu funksiya faqat main thread da ishlaydi
@MainActor
func uiYangilash(maqolalar: [String]) {
// UI bilan ishlash — xavfsiz
print("UI yangilandi: \(maqolalar.count) ta maqola")
}
}
// ═══════════════════════════════════════
// 3. FAQAT BITTA PROPERTY GA
// ═══════════════════════════════════════
class AralashClass {
@MainActor var uiMatni = "" // faqat main thread da
var backgroundQiymati = 0 // istalgan thread da
}
MainActor.run — qo'lda main thread ga o'tish
Ba'zan @MainActor qo'yilmagan funksiya ichidan UI ni yangilash kerak bo'ladi. MainActor.run { } bloki kodni main thread ga o'tkazadi. Bu blok tugagandan keyin kod yana avvalgi thread ga qaytadi. Bu DispatchQueue.main.async { } ning zamonaviy almashtirgichi.
// ═══════════════════════════════════════
// MainActor.run — background dan main ga
// ═══════════════════════════════════════
func backgroundIsh() async {
// Bu background thread da
let natija = ogirHisoblash()
// Main thread ga o'tish
await MainActor.run {
// Bu main thread da — UI yangilash xavfsiz
print("UI yangilandi: \(natija)")
}
// Yana background ga qaytdi
let boshqaNatija = boshqaOgirIsh()
}
func ogirHisoblash() -> Int { return 42 }
func boshqaOgirIsh() -> Int { return 100 }
SwiftUI da amaliy misol
SwiftUI da ObservableObject ViewModel ni @MainActor bilan belgilash eng to'g'ri usul. @Published property lar UI thread da yangilanishi shart — @MainActor buni kafolatlaydi. nonisolated metodlar esa og'ir hisoblashlarni background da bajarish uchun ishlatiladi — main thread ni band qilmaydi.
import SwiftUI
// ═══════════════════════════════════════
// SWIFTUI + @MAINACTOR VIEWMODEL
// ═══════════════════════════════════════
@MainActor
class VazifalarViewModel: ObservableObject {
@Published var vazifalar: [String] = []
@Published var yuklanmoqda = false
@Published var xatoXabari: String?
func yuklash() async {
yuklanmoqda = true // ✅ main thread
xatoXabari = nil
do {
// await — tarmoq ishi background da
let yangilar = try await tarmoqdanYukla()
vazifalar = yangilar // ✅ main thread ga qaytdi
} catch {
xatoXabari = error.localizedDescription
}
yuklanmoqda = false // ✅ main thread
}
// nonisolated — bu metod background da ishlashi mumkin
// Main thread ni band qilmaydi
nonisolated func tarmoqdanYukla() async throws -> [String] {
try await Task.sleep(for: .seconds(2))
return ["Vazifa 1", "Vazifa 2", "Vazifa 3"]
}
}
struct VazifalarKorinishi: View {
@StateObject private var vm = VazifalarViewModel()
var body: some View {
List(vm.vazifalar, id: \.self) { vazifa in
Text(vazifa)
}
.overlay {
if vm.yuklanmoqda { ProgressView() }
}
.task { await vm.yuklash() }
}
}
🎯 Topshiriq
@MainActor qo'yilgan HisobViewModel yarating: @Published var balans: Double. yuklash() async metodi 2 soniya kutib balansni yangilasin. nonisolated metod bilan og'ir hisoblashni background da bajaring. SwiftUI View da ProgressView va Text bilan ko'rsating.