Published on

Global Actor va @MainActor bilan ishlash

Authors

Global Actor nima?

Oldingi darslarda biz bitta qiyin vazifani bajaruvchi (ma'lumotlarni xavfsiz saqlovchi) oddiy actor yaratishni ko'rgan edik. Lekin tasavvur qiling, siz o'sha bitta xavfsiz oqimni (thread'ni) butun ilovangiz bo'ylab turli xil klasslarda, struct'larda, funksiyalarda ishlatmoqchisiz. Ya'ni, ilovaning boshqa qismlarida ham ba'zi muhim mantiqlarni aynan bitta aniq Actor izolyatsiyasi ostida himoyalamoqchisiz.

Bunday hollarda oddiy Actor yetarli bo'lmaydi va bizga Global Actor yordamga keladi. U butun dastur bo'ylab bitta yagona (Singleton) havola orqali ishlaydi.

Global Actor yaratish

Global Actor yaratish uchun biz maxsus @globalActor protokolidan va ichida bitta shared yagona nusxa (instance) ga ega struct'dan foydalanamiz.

// 1. Dastlab oddiy actor yaratamiz
actor MyDataManager {
    func getDatabaseData() -> [String] {
        return ["Bir", "Ikki", "Uch"]
    }
}

// 2. Ana endi uni Global Actor'ga aylantiramiz
@globalActor
struct MyFirstGlobalActor {
    static let shared = MyDataManager() // Yagona havola (Singleton)
}

Global Actor'ni ishlatish

Global Actor tayyor. Endi biz ilovaning xohlagan joyidagi oddiy funksiyani olib, uni bemalol shu Actor himoyasiga o'tkazishimiz mumkin! Buni qilish uchun funksiyaning oldiga @MyFirstGlobalActor kalit so'zini qo'shishimiz kifoya.

class ViewModel {
    // Ushbu funksiya endi oddiy oqimda emas, aynan MyFirstGlobalActor
    // himoyasi (izolyatsiyasi) ostida ishlaydi!
    @MyFirstGlobalActor
    func getHeavyData() {
        // Og'ir operatsiyalar...
    }
}

Bunday funksiyani chaqirayotganda endi tizim avtomatik ravishda sizdan await bilan kutishni talab qiladi, chunki bu funksiya global himoyada.

@MainActor – Eng mashhur Global Actor

Apple tizimida UI (Ekranga oid narsalar) har doim Asosiy Oqimda (Main Thread) yangilanishi shart ekanligini yaxshi bilamiz. Agar fonda ishlab turib uyni yangilamoqchi bo'lsak, avvallari DispatchQueue.main.async yozib qiynalardik. Endi-chi?

Apple biz uchun tayyor global actor yaratib qo'ygan va u @MainActor deb ataladi!

@MainActor ni qayerlarda ishlatamiz?

Qachonki bizning o'zgaruvchimiz yoki funksiyamiz to'g'ridan-to'g'ri Ekranni o'zgartirsa, uni asosiy oqimda ishlashiga majbur qilishimiz kerak.

class HomeViewModel: ObservableObject {
    @Published var dataArray: [String] = []

    func fetchData() async {
        // Ma'lumotlarni fonda yuklaymiz (masalan internetdan)
        let newData = ["Olma", "Nok"]

        // Ekranni yangilash uchun MainActor'ga o'tamiz
        await MainActor.run {
            self.dataArray = newData
        }
    }
}

Butun bir Class'ni @MainActor qilish

Agar ViewModel'imizdagi deyarli barcha funksiya va o'zgaruvchilar UI (ekran) bilan bog'liq bo'lsa, har biriga alohida @MainActor yozib o'tirish zerikarli. Buning o'rniga biz butun boshli klassning o'zini MainActor qilib belgilashimiz mumkin:

@MainActor // Bu klassdagi hamma narsa faqat Asosiy Oqimda ishlaydi
class ProfileViewModel: ObservableObject {
    @Published var name: String = ""
    @Published var age: Int = 0

    // Bu funksiya avtomatik main thread'da ishlaydi
    func updateProfile() {
        self.name = "Ali"
    }

    // Agar qaysidir funksiya UI ga umuman aloqasi bo'lmasa va
    // main thread'ni qotirib qo'ymasligi uchun uni mustaqil qilishimiz mumkin:
    nonisolated func doBackgroundMath() {
        print("Men fonda ishlayveraman")
    }
}

Xulosa: Global actorlar yirik dasturlarda kod xavfsizligini ta'minlashning zo'r arxitekturaviy usulidir. Ularning eng asosiysi bo'lgan @MainActor esa har qanday SwiftUI va UIKit dasturchisining kundalik eng muhim quroliga aylanib ulgurgan!

Buy mea coffee