Published on

Swift-da Class-lardan qanday foydalanish kerak

Authors

Hammaga xush kelibsiz! Bu videoda biz class-larni ko'rib chiqamiz. O'tgan ikki videoda struct va enum-larni o'rgandik β€” ular ikkalasi ham value type edi. Endi esa reference type bo'lgan class-larga o'tamiz.

Reference type-lar value type-lardan tubdan farq qiladi va shu sababli ulardan foydalanish ham biroz boshqacha. Aslida, class bilan ishlash struct'ga qaraganda biroz osonroq. Bu class-ni doim afzal ko'rish kerak degani emas β€” ko'p hollarda struct yaxshiroq tanlov. Lekin ayrim hollarda class ishlatish majburiy bo'ladi.


Stack vs Heap β€” qisqa eslatma

// Struct-lar:
// - Tezkor
// - Stack-da saqlanadi
// - Value type β€” nusxa ko'chiriladi va mutatsiya qilinadi

// Class-lar:
// - Struct-larga qaraganda sekinroq (lekin baribir juda tez)
// - Heap-da saqlanadi
// - Heap dasturning barcha thread-lari o'rtasida umumiy
// - Reference type β€” xotiradagi obyektga ishora qiladi
//   va shu obyektni o'zgartiradi, yangi nusxa yaratmaydi

Class yaratilganda u xotirada (Heap-da) saqlanadi. Bizda esa shu xotiradagi obyektga pointer (ishora) bo'ladi. Biz obyektni o'zgartirmoqchi bo'lganimizda uni ko'chirmay, to'g'ridan-to'g'ri xotiradagi asl nusxasini o'zgartiramiz.


Class yaratish

Keling, oddiy view model yarataylik. ViewModel β€” bu MVVM (Model-View-ViewModel) arxitekturasidan kelgan tushuncha. Swift UI-da view model odatda class bo'ladi.

class ScreenViewModel {
    var title: String
    var showButton: Bool

    init(title: String, showButton: Bool) {
        self.title = title
        self.showButton = showButton
    }
}

Struct va class init farqi

Struct-da implicit init bor edi β€” biz init yozmasak ham kompilyator uni o'zi tushunardi. Class-da esa bunday imkoniyat yo'q, shuning uchun init-ni o'zimiz yozishimiz shart:

// Struct β€” implicit init mavjud, yozmasak ham ishlaydi
struct CarModel {
    var brand: String
    var model: String
}

// Class β€” init yozilmasa kompilyator xato beradi
class ScreenViewModel {
    var title: String
    var showButton: Bool

    // Bu init bo'lmasa: "Class has no initializers" xatosi
    init(title: String, showButton: Bool) {
        self.title = title
        self.showButton = showButton
    }
}

Yoki xususiyatlarga default qiymat bersak, init yozmasak ham bo'ladi:

class ScreenViewModel {
    var title: String = "Salom"
    var showButton: Bool = true
    // init kerak emas
}

Deinit β€” obyektni yo'q qilish

Class-ning struct-dan yana bir farqi β€” deinit mavjudligi:

class ScreenViewModel {
    var title: String
    var showButton: Bool

    init(title: String, showButton: Bool) {
        self.title = title
        self.showButton = showButton
    }

    deinit {
        // Bu kod obyekt xotiradan o'chirilayotganda ishlaydi
    }
}

// Struct-larda deinit YO'Q
// Chunki struct-lar value type β€” har o'zgarishda yangi nusxa yaratiladi
// Class-larda esa bir xil obyekt tirik turadi, faqat ichidagi qiymatlar o'zgaradi

deinit kam ishlatiladi. Ko'pgina ilovalar yozilganda umuman kerak bo'lmasligi mumkin, lekin u faqat class-larga xos ekanini bilish muhim.


Instance yaratish va qiymat o'zgartirish

// Class instance yaratish
let myViewModel = ScreenViewModel(title: "Ekran 1", showButton: true)

E'tibor bering β€” bu yerda let ishlatildi, var emas. Struct-larda qiymatni o'zgartirish uchun var kerak edi. Class-larda esa obyektning o'zi o'zgarmaydi β€” ichidagi ma'lumotlar o'zgaradi:

let myViewModel = ScreenViewModel(title: "Ekran 1", showButton: true)

// Bu ishlaydi β€” obyektning o'zi emas, ichidagi qiymat o'zgarmoqda
myViewModel.showButton = false

// Izoh:
// myViewModel β€” bu xotiradagi obyektga ishora (pointer)
// Pointer o'zgarmayapti (shuning uchun let)
// Xotiradagi obyekt ichidagi qiymat o'zgarmoqda

Bu struct bilan ishlashdan tubdan farq qiladi β€” struct-da mutating funksiyalar yozib, yangi nusxa yaratishimiz kerak edi.


Class ichida funksiyalar yozish

Qiymatlarni class tashqarisidan o'zgartirish yaxshi amaliyot emas. Buning o'rniga o'zgartirishlarni class ichidagi funksiyalar orqali amalga oshirish kerak:

class ScreenViewModel {
    private(set) var title: String
    private(set) var showButton: Bool

    init(title: String, showButton: Bool) {
        self.title = title
        self.showButton = showButton
    }

    func hideButton() {
        showButton = false
    }

    func updateShowButton(newValue: Bool) {
        showButton = newValue
    }

    deinit {
        // Obyekt xotiradan o'chirilayotganda ishlaydi
    }
}

private(set) nima beradi?

let myViewModel = ScreenViewModel(title: "Ekran 1", showButton: true)

// Bu ishlaydi β€” qiymatni olish mumkin
let currentValue = myViewModel.showButton

// Bu XATO beradi β€” tashqaridan qiymat o'rnatib bo'lmaydi
myViewModel.showButton = false // Xato!

// To'g'ri yo'l β€” class ichidagi funksiya orqali
myViewModel.hideButton()
myViewModel.updateShowButton(newValue: false)

Bu clean code β€” toza kod yozish amaliyoti. Ma'lumotlarni o'zgartirish mantig'i faqat class ichida bo'lishi kodni tushunarli va xavfsiz qiladi.


Struct vs Class β€” qisqa taqqoslash

XususiyatStructClass
Xotirada joyiStackHeap
TuriValue typeReference type
TezlikTezroqBiroz sekinroq
Thread xavfsizligiHaYo'q (ehtiyot kerak)
Implicit initHaYo'q
DeinitYo'qHa
Qiymat o'zgartirishMutating / yangi nusxaBevosita obyekt ichida
O'zgaruvchi turivar keraklet bilan ham o'zgaradi

Xulosa

Class-lar struct-larga qaraganda ishlatish jihatidan biroz osonroq β€” mutating yozmasak ham bo'ladi, let bilan ham ichidagi qiymatlarni o'zgartirish mumkin. Lekin bu class-ni doim tanlash kerak degani emas. Struct-lar tezroq, thread-safe va xotiradan samaraliroq foydalanadi.

Keyingi videolarda struct, enum va class-larni haqiqiy stsenariyda qo'llaymiz. Undan oldin esa private kalit so'zini chuqurroq o'rganamiz.

Rahmat, men Nick, bu Swiftful Thinking va keyingi videoda ko'rishguncha!

Buy mea coffee