Published on

Swift uchun Object-Oriented Programming (OOP) nima

Authors

Hammaga yana xush kelibsiz! Ushbu videoda biz object-oriented programming (OOP, ya'ni obyektga yo'naltirilgan dasturlash) nima ekanligiga chuqur kirib boramiz β€” bu mavzuni aynan Swift va iOS ilova yaratishga bog'liq holda ko'rib chiqamiz.

Shuni alohida ta'kidlamoqchiman: ushbu videoda gaplashadigan narsalarning ko'pini, agar siz endi kod yozishni o'rganayotgan bo'lsangiz, to'liq tushunib olishingizni kutmayman, va menimcha videoning oxirida sizda bu haqida mukammal va to'liq tushuncha bo'lishi ham shart emas. Mening maqsadim β€” sizlarni shunchaki shu mavzular bilan tanishtirish, toki hech bo'lmaganda ongingizning bir burchagida kodimizda haqiqatda nima bo'layotgani haqida yuqori darajadagi tushuncha bo'lsin. Bu, umuman olganda, sizlarga kod o'rganish yo'lida yordam beradi deb o'ylayman.

Shunday qilib, biz obyektga yo'naltirilgan dasturlashni juda yuqori darajada (high level) ko'rib chiqamiz. Gaplashadigan mavzularimiz: obyektlarni yaratish va yo'q qilish (obyektni qanday initialize yoki de-initialize qilish, va obyektni xotiraga joylashtirish nimani anglatadi β€” ya'ni xotiradan joy ajratish yoki bo'shatish). So'ngra biz xotiraning o'zi β€” ya'ni stack va heap haqida gaplashamiz. Bularning farqini eng chuqur darajada bilishingiz shart emas, ammo hech bo'lmaganda yuqori darajadagi tushunchaga ega bo'lish keyingi bir necha videoda bizga yordam beradi.

Xotiraning bu ikki turini ko'rib chiqar ekanmiz, biz ularning ichida joylashgan obyekt turlariga ham to'xtalamiz β€” struct va class deb ataladigan narsalarga. Bu esa bizni keyingi videoga olib boradi: keyingi video aynan structlar haqida, undan keyingisi esa classlar haqida bo'ladi. Bular β€” bizning kodimizda yarata oladigan maxsus ma'lumot turlari: xuddi string va boolean-larimiz bo'lgani kabi, biz o'zimizning turlarimizni ham yaratishimiz mumkin. Ammo muammo shundaki, men sizlarga avval bu turlar kodimizda, "parda ortida" aslida nimani anglatishini tushuntirib bermasdan, ularni yaratishni boshlashni xohlamayman. Shuning uchun aynan shu video β€” struct va class haqidagi videolardan oldin keladi: bu shunchaki OOP-ga, stack va heap nimaligiga, multi-threading nimaligiga yuqori darajadagi kirish, ammo biz hech qaysi birini chuqur tahlil qilmaymiz.

Bu β€” qiyin video bo'ladi, ammo biz hech qanday kod yozmaymiz. Sizdan buni to'liq tushunib olishni kutmayman β€” shunchaki orqaga yaslanib, bu narsalar haqida yuqori darajada o'ylab ko'ring, va keyingi bir necha videoda biz shu obyektlarni haqiqatan yaratishni boshlaganimizda, ko'pchiligi o'z-o'zidan tushunarli bo'lib qoladi.


Va biz qaytib keldik! Bu pleylist, menimcha, men kutganimdan ham qiyinroq chiqdi, ammo shu bilan birga biz Swift-da bilishimiz kerak bo'lgan narsalarni qamrab olmoqdamiz. Shuning uchun bu qiyin bo'lsa-da, menimcha buni birinchi marta bosib o'tish β€” eng qiyin qismi, va keyin biz haqiqiy kod yozib, ilova qurishni boshlaganimizda, bu ancha osonroq va qiziqarliroq bo'lib qoladi.

Kimki shu pleylistni kuzatib borayotgan bo'lsa, shuni alohida ta'kidlab o'tmoqchiman: bundan keyingi pleylist β€” "SwiftUI Boot Camp" deb ataladi, va u butunlay UI (interfeys)ga asoslangan pleylist β€” u yerda biz haqiqatan ham qiziqarli ekranlar quramiz, va bu ushbu pleylistdan ancha qiziqarliroq va jonliroq bo'ladi. Ushbu pleylist esa Swift-ning sintaksisini qamrab oladi, toki siz yozayotgan kodning haqiqiy texnik ma'nosini tushunib olasiz. Buni alohida ta'kidlayapman, chunki ba'zi odamlar shu joyda "men buni hech qachon o'rganib bo'lmayman" deb umidsizlanib, pleylistni tashlab ketishini xohlamayman. Biz qasddan biroz qiyinroq mavzulardan boshlaymiz, chunki tilning o'zini chuqur o'rganishni xohlaymiz, ammo ushbu pleylistni tugatgach, keyingi pleylistda ekranlar qurishni boshlaganingizda, kod aslida ushbu pleylistdagidan ancha osonroq bo'lib chiqadi β€” chunki ekran qurayotganimizda, ekran qanday yangilanayotganini, rang qizil yoki ko'k ekanligini real vaqtda ko'rib turamiz, va bu, rostini aytsam, bu mavzularni o'rganishdan ancha qiziqarliroq.

Shuning uchun, agar shu pleylistni kuzatib borayotgan bo'lsangiz, iltimos o'zingizni bosib qolmang. Biz qasddan shu mavzularga sho'ng'iyapmiz, toki bu narsalarning bir qismi sizning asboblar qutingizga kirib qolsin, ammo va'da beraman: shu pleylistni tugatganingizdan so'ng, keyingi pleylist sizga juda oson tuyuladi.


Ushbu videoda nima qilamiz

Ushbu video, aslida, deyarli hech qanday kod yozishni o'z ichiga olmaydi β€” biz asosan gaplashamiz. Shuning uchun bu, ehtimol, ushbu pleylistdagi eng "zerikarli" video bo'lishi mumkin, ammo o'zimizning ma'lumot turlarimizni yoza boshlashdan oldin gaplashishimiz kerak bo'lgan juda muhim narsalar bor.

Navigator-da o'ng tugmani bosib, yangi playground sahifa yaratamiz va unga Object Oriented Programming deb nom beramiz. Bu yerda kod deyarli yozmaymiz, ammo sizlar uchun juda ko'p izoh qoldiramiz.


Obyektlarni yaratish va yo'q qilish

Object-oriented programming (obyektga yo'naltirilgan dasturlash) haqida gaplashamiz, va buni aynan Swift-ga bog'liq holda ko'rib chiqamiz.

Ilovangizning (yoki istalgan ilovaning) hayoti davomida biz obyektlarni yaratamiz va yo'q qilamiz. Bu degani β€” biz haqiqatan ham obyektni yaratadigan kod yozamiz, va keyin bu obyektga endi ehtiyoj qolmaganda, biz uni o'chiramiz yoki yo'q qilamiz.

"Yaratish" (create) β€” bu aslida texnik atama emas. Haqiqiy texnik atama β€” obyektni initialize qilish, va kodda bu init deb ataladi. Siz kod yozayotganingizda va darsliklarni tomosha qilayotganingizda bu init so'zini juda ko'p uchratasiz β€” demak, init obyektni birinchi marta yaratishni bildiradi.

Obyektni yaratganimizda (initialize qilganimizda), biz aslida uni xotiraga joylashtiramiz (allocate). Demak:

Yaratish (create) = initialize qilish (init) = xotiraga joylashtirish (allocate).

Obyekt yaratilganda, u xotiraga initializer orqali kiradi β€” initializer shunchaki obyektni yaratadigan birinchi funksiya, xolos.

Keyinroq biz obyektlarni yo'q ham qilamiz. Va yana, bu ham texnik atama emas β€” obyektni yo'q qilganimizda ishga tushadigan funksiya de-initialize deb ataladi, ya'ni deinit. Kodda deinitni alohida, qo'lda yozish odatda kamdan-kam uchraydi (men buni keyingi videolarda ko'rsataman), ko'pincha biz faqat init haqida o'ylaymiz, deinit esa avtomatik tarzda o'z-o'zidan bajariladi. Ammo deinit ishga tushganda, biz obyektni xotiradan bo'shatamiz (deallocate), ya'ni uni xotiradan olib tashlaymiz.


ARC β€” Automatic Reference Counting

Demak, biz obyekt yaratamiz, so'ngra uni yo'q qilamiz. Swift-da Automatic Reference Counting (qisqasi β€” ARC) deb ataladigan narsa mavjud β€” bu juda mashhur iOS intervyu savollaridan biri.

"Automatic Reference Counting" eshitilishidan ancha murakkab tuyulsa-da, aslida bu juda sodda: bu shunchaki xotiradagi obyektlarning jonli hisobi (live count). Bu, aynan, obyektlarni sanash kabi sodda: biz bitta obyekt yaratsak, hisob bittaga oshadi; ikkita obyekt yaratsak, hisob ikkiga oshadi; bitta obyektni yo'q qilsak, hisob bittaga kamayadi. Bu raketa fizikasi emas β€” agar yuzta obyekt yaratsangiz, hisobingiz yuzga yetadi.

Buni gaplashayotganimizning sababi shunda: xotirada obyektlar qancha ko'p bo'lsa β€” ya'ni hisob qancha yuqori bo'lsa β€” ilova shunchalik sekinroq ishlaydi.

Agar siz boshlang'ich darajadagi ilova yozayotgan bo'lsangiz va bir nechta oddiy ekranlaringiz bo'lsa, hech qanday murakkab narsa qilmayotgan bo'lsangiz, xotira haqida hozircha qattiq bosh qotirishingiz shart emas β€” iPhone juda kuchli qurilma, u xotira ilovangizni sekinlashtirishdan oldin juda ko'p hisob-kitobni bajara oladi. Ammo yuqori darajada shuni tushunishimiz kerak: biz bu hisobni iloji boricha past ushlab turishni xohlaymiz.

ARC hisobini iloji boricha past saqlashga harakat qilamiz β€” bu degani, ilovamiz baribir to'g'ri ishlashi sharti bilan, iloji boricha kam obyekt yaratamiz. Agar ilovani ishga tushirish uchun xotiraga obyekt qo'shishimiz kerak bo'lsa β€” qo'shamiz, bu muqarrar. Ammo agar hozir bizga aslida kerak bo'lmagan ortiqcha obyektlar yaratilayotgan bo'lsa, kodimizni shunday tuzatishimiz kerak: obyektlarni faqat kerak bo'lganda yarataylik, va ularga endi ehtiyoj qolmagan zahoti yo'q qilaylik.

Misol: ikki ekranli ilova

Aytaylik, ilovada ikki ekran bor, va foydalanuvchi birinchi ekrandan ikkinchi ekranga o'tmoqda. Ikkinchi ekranga o'tganimizda, biz ikkinchi ekranni faqat kerak bo'lganda yaratishni (allocate qilishni) xohlaymiz β€” masalan, foydalanuvchi ikkinchi ekranga o'tish uchun tugmani bosgan paytda. Aksincha, ikkinchi ekranga yetib borganimizda, ehtimol birinchi ekranni xotiradan bo'shatishni xohlaymiz.

Demak: ilova birinchi marta ochilganda, biz darrov ikkala ekranni ham yuklashimiz mumkin edi, ammo aslida foydalanuvchi ikkinchi ekranga o'tmaguncha, bizga ikkinchi ekran kerak emas. Shuning uchun ilova ochilganda faqat birinchi ekranni yuklaymiz, va foydalanuvchi tugmani bosgan paytda β€” ikkinchi ekran uchun xotira ajratamiz. Va agar ikkinchi ekranda bo'lsak-u, birinchi ekranga endi ehtiyoj qolmagan bo'lsa β€” birinchi ekranni bo'shatishimiz mumkin (shart emas β€” masalan, agar foydalanuvchi birinchi ekranga qaytishi mumkin bo'lsa, uni xotirada saqlab qolishni xohlashingiz mumkin).

Umuman olganda, kodimiz haqida shunday o'ylashimiz kerak: obyektlarni faqat kerak bo'lganda yaratamiz, va ularga ehtiyoj qolmagan zahoti olib tashlaymiz. Yana bir bor ta'kidlayman: agar shu kursni kuzatib borayotgan bo'lsangiz, keyingi 50 ga yaqin videoda biz xotira bilan deyarli ishlamaymiz β€” bu o'rta va yuqori darajadagi mavzu, va endi kod yozishni o'rganayotganda bunga ko'p vaqt sarflash shart emas. Ammo ekspert dasturchi bo'lish uchun, parda ortida nima bo'layotganini yaxshi tushunishingiz kerak.


Xotiraning ikki turi: Stack va Heap

Hozirgacha men xotiraga "qo'shish" haqida gapirdim, ammo aslida xotira ikki turga bo'linadi (kamida iPhone-da). Bu turlar β€” stack (steg) va heap (kuyma).

Eng muhim narsa shuni: faqat heapdagi obyektlar ARC hisobiga kiradi. Demak, obyekt soni oshib-tushishi haqida gapirganimizda, biz faqat heapdagi obyektlarni nazarda tutamiz β€” chunki stack va heap'dagi obyektlar orasida texnik farq mavjud.

Agar siz allaqachon Swift va xotira boshqaruvi bilan tanish bo'lsangiz, mening kanalimda shu mavzuga bag'ishlangan chuqur video bor β€” "Struct vs Class vs Actor, Value vs Reference Types, va Stack vs Heap" deb ataladi, va bu Swift Concurrency pleylistida joylashgan, davomiyligi bir yarim soat. Ammo agar siz endi kod yozishni o'rganayotgan bo'lsangiz, bu video sizga hozircha juda ilg'or bo'ladi β€” hozircha buni tomosha qilmasligingizni tavsiya qilaman.

Stack-dagi obyektlar

Stackdagi obyektlar β€” bu biz ushbu kurs davomida ishlatib kelgan asosiy turlarning ko'pchiligi: string-lar, boolean-lar, integer-lar, date-lar va shunga o'xshash boshqa asosiy turlar. Bizning hali ko'rib chiqmagan, ammo stack-ga tegishli yangi narsalarimiz β€” struct va enumlar, va men buni keyingi ikki videoda batafsil ko'rib chiqaman. Demak, biz o'z obyektlarimizni yaratishni boshlaganimizda, ular stackga tushadi.

Heap-dagi obyektlar

Buning aksi β€” heapdagi obyektlar. Bizning hozirgacha ko'rib chiqgan narsalarimizning ko'pi stack-da bo'lgan bo'lsa, funksiyalar aslida heapga tegishli, shuningdek biz hali ko'rmagan class va actorlar ham shunday. Ushbu pleylistda actorlarni ko'rib chiqmaymiz, chunki sizga uzoq vaqt davomida bu kerak bo'lmaydi (bu mening kanalimdagi Swift Concurrency pleylistida bor). Ammo classlarni biz albatta ko'rib chiqamiz, va class-lar struct-lardan boshqacha saqlanishini tushunish muhim. Struct va class orasidagi farq ko'pchilikni chalkashtiradi, ammo ular orasida ham kodda, ham xotirada saqlanish jihatidan tubdan farqlar mavjud, va bu sizning ilovangizga ta'sir qiladi.

Demak: heapda saqlangan obyektlar ARC hisobiga kiradi, stackda saqlangan obyektlar esa kirmaydi.


Multi-threading: stack va heap qanday bog'lanadi

iPhone-da kod ishlatganimizda, biz buni thread (oqim) ustida bajaramiz. Thread-ni, oddiy qilib, dvigatel (engine) sifatida tasavvur qilishingiz mumkin. iPhone β€” multi-threaded (ko'p oqimli) muhit, ya'ni bir vaqtning o'zida bir nechta thread ishlaydi. Agar mening ilovamda bir nechta dvigatel bo'lsa, men butun kodimni bitta dvigatelga joylashtirishim mumkin, yoki uni bir nechta dvigatelga bo'lib tashlashim mumkin β€” va, albatta, agar kod bir nechta turli thread-da bo'lsa, u biroz tezroq ishlaydi, chunki ular mustaqil ravishda harakat qila oladi.

Endi muhim qoida: har bir thread o'zining stack-iga ega, ammo barcha thread-lar uchun faqat bitta heap mavjud.

Demak: bizda multi-thread muhit bor β€” ilovamizda bir vaqtning o'zida bir nechta thread ishlaydi. Har bir thread o'zining stack-iga ega, ammo barchasi bitta umumiy heapdan foydalanadi.

Shu sababli stack β€” heap-dan tezroq

Bundan shunday xulosa chiqarishimiz mumkin: stack heap-dan tezroq, heap esa sekinroq β€” chunki heap barcha thread-lar uchun bitta, va shuning uchun barcha thread-lar bo'ylab sinxronlashtirilishi kerak. Tasavvur qiling: bir nechta turli thread bir xil ma'lumotga heap-da bir vaqtda murojaat qilmoqchi bo'lsa β€” bu muammo keltirib chiqarishi mumkin. Stack-da bunday muammo hech qachon yuzaga kelmaydi, chunki har bir thread mustaqil β€” har birining o'z stack-i bor. Shuning uchun, odatda, stack tezroq, heap esa sekinroq.

Xotira sarfi (memory footprint) bo'yicha ham xuddi shu mantiq amal qiladi: stack-ning xotira sarfi odatda pastroq, heap-ning xotira sarfi esa yuqoriroq.

Esingizda bo'lsin: heap-dagi obyektlar ARC hisobiga kiradi β€” demak, stack afzalroq. Agar biz heap o'rniga stack-ga tushadigan obyekt yaratish imkoniga ega bo'lsak, buni afzal ko'ramiz. Aynan shu sababdan, SwiftUI-da ishlaganingizda, har bir view (ekran) β€” struct ekanligini payqaysiz: chunki struct-lar tezroq va xotira sarfi past, va aynan shu sabab SwiftUI ilovalari yuqori darajada samarali (performant) ishlaydi.

Albatta, agar har doim faqat stack-dan foydalansak edi, biz shunday qilardik β€” ammo bu imkonsiz: ba'zan bizga funksiyalar va class-lar kerak bo'ladi, shuning uchun heap-dan ham foydalanishimiz shart. Demak, masala "bittasini tanlash" emas β€” biz farqni tushunishimiz va yaxshi ilova qurish uchun ikkalasini birga ishlatishimiz kerak.


Value turlar va reference turlar

Endi yana bir qadam ilgariga boraylik. Bizda stack va heap bor, multi-thread muhit borligini va har bir thread o'z stack-iga ega, ammo barchasi bitta heap-ni baham ko'rishini bilamiz. Endi mantiqan savol tug'iladi: nega ba'zi obyektlar stack-ga, ba'zilari esa heap-ga tushadi? Ular orasida texnik jihatdan nima farq bor, masalan, nega class-ni stack-ga qo'yib bo'lmaydi? Buning texnik sababi mavjud, va bu farqning tagida β€” value turlar va reference turlar tushunchasi yotadi.

Value turlar (stack)

Stackdagi obyektlar β€” value (qiymat) turlar. Value turni tahrirlaganingizda, siz, aslida, uning nusxasini (copy) yaratib, yangi ma'lumot bilan qayta yaratasiz. Masalan, agar bizda Boolean bo'lsa (stack-dagi obyekt β€” Boolean), va uning qiymati true bo'lsa, so'ngra uni falsega o'zgartirsak β€” kodda aslida nima bo'ladi? Biz birinchi versiyaning nusxasini olamiz, so'ngra uni o'zgartirib, yangi qiymatga ega yangi versiyasini yaratamiz.

Buni xuddi nusxalash va joylashtirish (copy-paste) kabi tasavvur qiling β€” faqat joylashtirilganda, yangi qiymat bilan joylashtiriladi. Demak, nusxalab-joylashtirganingizda, eski obyekt ham, yangi obyekt ham mavjud bo'ladi β€” bu siz o'zgartirgan aynan bir xil obyekt emas, balki siz nusxa olgan va yangi versiyasini joylashtirgan obyekt.

Reference turlar (heap)

Bunga qarama-qarshi β€” heapdagi obyektlar reference (havola) turlar. Reference turni tahrirlaganingizda, siz, aslida, havola qilayotgan obyektning o'zini tahrirlaysiz.

Men yuqorida "reference turni tahrirlaganingizda siz havola qilayotgan obyektning o'zini tahrirlaysiz" dedim β€” bu havola (reference) aslida pointer (ko'rsatkich) deb ataladi, chunki u xotiradagi heap'da joylashgan obyektga ko'rsatib turadi. Demak, heap-dagi biror obyektga havola qilganimizda, biz aslida heap xotirasidagi shu obyektga ko'rsatib (pointing) turamiz, va biz shu obyektni o'rnida turib o'zgartiramiz.

Value tur β€” nusxalab, yangi versiyasini joylashtiramiz. Reference tur β€” mavjud obyektning o'ziga kirib, uni o'rnida tahrirlaymiz, yangi nusxa yaratmaymiz.

Bu β€” value va reference turlar orasidagi tub farq.

Nega bu farq stack va heap-ga bog'liq

Agar yuqorida gaplashgan thread-lar haqidagi fikrga qaytsak: har bir thread o'z stack-iga ega. Tasavvur qiling, sizda bir thread-da ma'lumot bor, va siz uni keyingi thread-ga, keyin yana keyingisiga ko'chirishni xohlaysiz β€” bunday holatda yangi qiymatni keyingi thread-ga nusxalab-joylashtirish juda oson, chunki obyekt avval qayerda bo'lganini bilishimiz shart emas β€” biz shunchaki uni yangi thread-ga nusxalab qo'ya olamiz. Shuning uchun value turlar stack-da bo'la oladi β€” chunki ularni barcha turli thread-larga osongina nusxalab-joylashtirishimiz mumkin.

Heap esa β€” uni tahrirlaganimizda yoki ko'chirganimizda, biz har doim xotiradagi bir xil joyga ishora qilamiz. Aynan shuning uchun heap bittadir: ma'lumot birinchi thread-da bo'lsin, ikkinchi yoki uchinchi thread-da bo'lsin β€” uni bir thread-dan ikkinchisiga ko'chirganimizda, biz boshqa nusxa yaratmaymiz, faqat xotiradagi bir xil obyektga ishora qiluvchi pointerni baham ko'ramiz. Demak, bir nechta thread shu ma'lumotdan foydalansa ham, barcha thread-lar xotiradagi bir xil pointerga ishora qiladi.

Aynan shu β€” bu yerdagi tub farq. Yana bir bor: stack tezroq, heap sekinroq, ammo ikkalasi uchun ham foydalanish holatlari mavjud, va biz shunchaki yuqori darajada bu farqlarni tushunishimiz kerak. Stack-lar, mohiyatan, value turlar bo'lgani uchun, ularni shunchaki nusxalab-joylashtirishimiz mumkin β€” bu yerda thread bilan bog'liq muammolar xavfi yo'q. Heap esa barcha thread-lar o'rtasida baham ko'rilgani uchun, bu yerda thread bilan bog'liq muammolar xavfi mavjud.


Birinchi struct va class-imiz

Videoni yakunlashdan oldin, sizlarga ikkita misol ko'rsatib o'taman. Bizda stackdagi obyekt β€” struct, heapdagi obyekt esa β€” class ekanligini bilamiz. Keling, hozir bittadan struct va class yarataylik:

struct MyFirstObject {
    var title: String = "Hello world"
}

class MySecondObject {
    var title: String = "Hello world"
}

Payqadingizmi β€” bular aynan bir xil ko'rinishga ega! Aynan shuning uchun struct va class-ni tushunish ancha qiyin: ular kodda bir xil ko'rinadi. Men buni class ham, struct ham qilib yozishim mumkin β€” ikkisi ham kompilyatsiya bo'ladi, va hech narsa bizga bu to'g'ri yoki noto'g'ri ekanligini aytmaydi. Demak, tashqi ko'rinishidan, kod yozayotganingizda, hech narsa sizga bu ikkisi orasidagi farqni ko'rsatmaydi. Ammo endi siz bilasiz: struct β€” value tur, class β€” reference tur. Keyingi 3-5 videoda struct va class haqida chuqur to'xtalamiz, chunki ular turli xil turlar bo'lgani uchun, kodimizda ulardan boshqacha foydalanishimiz kerak bo'ladi.


Metafora: maktab, sinf xonasi va test

Yakunlashdan oldin, sizlarga ushlab qolishga yordam beradigan kichik bir metafora taklif qilmoqchiman: classni va structni beshyoshli bolaga tushuntirib bering, deb tasavvur qilaylik. Hech kimni xafa qilmoqchi emasman, ammo buni iloji boricha soddalashtirib ko'rsataman. Albatta, har qanday metaforada bo'lgani kabi, bu yerda ham ba'zi joylarda mantiq buziladi va u mukammal emas, ammo menimcha buni hech bo'lmaganda sinab ko'rish kerak β€” chunki men o'zim kod o'rganayotganimda, struct va class orasidagi farqni haqiqatan tushunib olishim uchun bir yarim yil vaqt ketgan, va kimdir menga shu metaforani o'zimning dasturchilik yo'limning boshida aytib bersa, juda yaxshi bo'lardi β€” chunki kod o'rganayotganingizda, ko'p vaqt davomida shunchaki "buni class qilamanmi yoki struct qilamanmi" deb ikkilanib, qaysi biri to'g'ri ekanligini haqiqatan bilmasdan yurasiz.

Keling, shuni sodda metafora bilan tushuntiraylik: tasavvur qiling, bir maktab bor, va maktabda sinf xonalari mavjud. Har bir sinf xonasida o'qituvchi testlar tarqatadi. Kun davomida o'qituvchi turli sinflarga turli xil testlar tarqatadi β€” aytaylik, kun davomida besh xil sinf bo'ladi, har birida boshqacha test bo'ladi, va o'quvchilar testlarni javoblab, o'qituvchiga qaytarib beradi.

Bu metaforada:

  • Maktab β€” bizning ilovamiz
  • Sinf xonasi β€” class (bu so'zlarning ham bir xilligi tasodif emas)
  • Test β€” struct

Xuddi maktabda ko'plab turli narsalar bo'lib turgani kabi, biz ilova yaratamiz, va ilova ichida ham ko'plab turli narsalar bo'lib turadi.

Class β€” xuddi sinf xonasi kabi: biz class yaratamiz, va so'ngra shu "xona" ichida turli amallarni bajaramiz. Bu β€” heap, ya'ni biz uni o'zgartirganimizda, biz uni o'rnida turib tahrirlaymiz β€” sinf xonasi o'zi qimirlamaydi, u joyida turadi, va biz shu xona ichida harakat qilamiz. Shu amallardan biri β€” o'quvchilarning test topshirishi bo'lishi mumkin.

Test esa β€” struct: testlar tarqatilganda, biz testning 50 nusxasini yasab, barcha o'quvchilarga tarqatamiz β€” bu, aynan, value turning o'zi: bular kichik ma'lumot bo'laklari, va biz ularni ilova bo'ylab osongina ko'chirib yura olamiz.

Demak: sinf xonasi aslida qimirlamaydi β€” biz uni bir marta yaratamiz, va so'ngra ichida ko'plab amallarni bajaramiz. Test esa doimo harakatda: u ko'chiriladi, o'zgartiriladi, vaqti-vaqti bilan tahrirlanadi. Shuning uchun struct-larni biz osongina yasab, ilova bo'ylab ko'chirib, tarqatib yura olamiz, sinf xonasi (class) bilan esa buni qilish ancha qiyinroq.

Mana shu kabi: bizda sinf xonasi bor, va sinf xonasi ichida ko'plab amallar sodir bo'ladi β€” kodda biz class yaratamiz va class ichida amallarni bajaramiz. Bu misolda esa ko'plab turli xil testlar mavjud β€” har bir test bir oz boshqacha, har bir testda turli o'quvchining boshqacha nomi yozilgan, va o'qituvchi testlarni tarqatib, o'quvchilar esa ularni javoblab, o'qituvchiga qaytarib beradi. Demak, kodda biz ko'plab struct yaratib, ularni ilovamiz bo'ylab osongina tashib yuramiz β€” testni sinf xonasidan ko'ra ko'chirib yurish ancha oson, struct-ni class-dan ko'ra ko'chirib yurish ham ancha oson.

Bir eslatma: bu metafora mukammal emas. Texnik jihatdan, test (quiz)ni class qilib ham yasash mumkin β€” har bir test sarlavhaga ega bo'lardi, va sizga hech kim bu qanday bo'lishi "to'g'ri" yoki "noto'g'ri" ekanligini aytmaydi. Ammo umuman olganda, ilova yaratganingizda, struct-ni string, boolean, integer kabi boshqa value turlarga o'xshash deb o'ylang β€” kichik ma'lumot bo'lagi, biz uni yaratamiz, ehtimol tahrirlaymiz, ilova bo'ylab tashib yuramiz, ehtimol bir nechta turli "sinf xonalarida" ishlatamiz. Class-larni esa, garchi ularni ham tahrirlash mumkin bo'lsa-da, ilovamizda biroz barqarorroq bo'lgan, ilova bo'ylab ko'p tashib yurilmaydigan narsalar uchun ishlatamiz.


Qachon class, qachon struct ishlatish kerak

Yakunlovchi qoida sifatida:

Class quyidagilar uchun ishlatiladi (agar bu pleylistni kuzatib borayotgan bo'lsangiz, bularning ko'pi sizga hozircha notanish bo'lishi mumkin, ammo darslikda manager, data service, service, factory, yoki view model (agar MVVM-ni o'rgansangiz) kabi narsalarni ko'rsangiz β€” bularning barchasi class bo'ladi):

Bular β€” biz yaratadigan va ichida amallar bajaradigan obyektlar.

Struct esa quyidagilar uchun ishlatiladi:

Ma'lumot modellari (data models). Xuddi string yoki boolean haqida o'ylaganingizdek β€” ilovamiz bo'ylab tashib yurmoqchi bo'lgan ma'lumot bo'lagi. Bular β€” biz yaratadigan va ilovamiz bo'ylab tashib yuradigan obyektlar.

Albatta, texnik jihatdan ikkala tur obyektni ham ilova bo'ylab tashib yurish mumkin, ammo biz allaqachon bilganimizdek β€” har bir thread o'z stack-iga ega bo'lgani uchun, value turlarni ilova bo'ylab ko'chirish ancha osonroq, chunki biz yangi ma'lumotni shunchaki nusxalab-joylashtiramiz, ma'lumotni o'rnida tahrirlash o'rniga.


Xulosa

Bu β€” juda ko'p gapirish bo'ldi, ishonchim komil, ko'pchilik hozir biroz chalkashib qolgan bo'lishi mumkin. Aytishim kerakki, keyingi bir necha video aynan structlarga bag'ishlangan bo'ladi, undan keyin esa enum (bular ham, aytib o'tganimdek, stack-ga tegishli) haqida butun bir video, va so'ngra classlar haqida butun bir video bo'ladi. Shuning uchun, keyingi bir necha videodan so'ng, bu mavzu sizlarga ancha ravshanroq bo'lib qoladi. Men bularning barchasini oldindan ochiq aytib qo'yishni muhim deb hisobladim, toki siz bu haqida o'ylab boring.

Hozirgi bosqichda sizdan bularning hech birini chuqur darajada tushunishingizni kutmayman. Men shunchaki sizga bu ikkisi orasida farqlar mavjud ekanligini, va kod yozganimizda, yaratayotgan obyektlarimiz struct yoki class bo'lishini o'ylab ko'rishimiz kerak ekanligini tushunishingizni xohlayman, chunki qaysi birini tanlashimizga qarab, ilovamizda oqibatlar bo'ladi.

Agar bu mavzu sizni juda chalkashtirib yuborgan bo'lsa β€” yoki aksincha, sizga foydali bo'lgan bo'lsa β€” izohlarda yozib qoldiring. Bu β€” haqiqatan ham murakkab mavzu, va sizdan birinchi ko'rishda buni to'liq tushunib olishingizni kutmayman. Ammo yuqori darajada shuni tushunish kerak:

  • iOS-da biz obyektga yo'naltirilgan dasturlashdan foydalanamiz, va ilovamiz hayoti davomida obyektlarni yaratamiz va yo'q qilamiz.
  • Swift Automatic Reference Counting (ARC) deb ataladigan narsadan foydalanadi β€” har safar obyekt yaratganimizda hisob oshadi, va biz xotira sarfimizni iloji boricha past ushlab turishga harakat qilamiz, chunki bu ilovani yanada samaraliroq qiladi.
  • Xotira ikki qismga bo'linadi: stack va heap.
  • Stackdagi obyektlar β€” string, boolean, integer, struct kabi, ya'ni siz ilova bo'ylab tashib yuradigan deyarli har qanday ma'lumot turi.
  • Heapdagi obyektlar β€” class va funksiyalar kabi, ya'ni biroz barqarorroq narsalar, biz kod yozib, shu kod ichida amallarni bajaramiz.
  • iPhone β€” multi-threaded muhit, ya'ni bir vaqtning o'zida bir nechta thread ishlaydi, va biz ilovamizni yanada samaraliroq qilish uchun shu thread-lardan foydalanamiz. Ammo faqat stackdagi obyektlar turli thread-larda mustaqil harakat qila oladi; heapdagi obyektlar (class-lar kabi) barcha thread-lar bo'ylab baham ko'riladi, va shu sababli ular biroz sekinroq.
  • Shuning uchun, agar imkonimiz bo'lsa, stackdagi obyektlardan foydalanishni afzal ko'ramiz, chunki ular tezroq va xotira sarfi pastroq. Ammo ba'zan heapdagi obyektlarga ham ehtiyojimiz bor β€” ular biroz sekinroq, xotira sarfi yuqoriroq bo'lsa-da, bizga ma'lum holatlarda albatta kerak bo'ladi.
  • Stack va heap orasidagi asosiy farq β€” bu obyekt value tur yoki reference tur ekanligida. Value turlarni tahrirlaganimizda, biz nusxalab, yangi versiyasini joylashtiramiz. Reference turlarni tahrirlaganimizda esa, biz nusxalamaymiz β€” xotiradagi obyektga ishora qilib, uni o'rnida tahrirlaymiz.
  • Aynan shu β€” struct (value tur) va class (reference tur) orasidagi asosiy farq.

Juda-juda qiyin video bo'ldi, ammo keyingi bir necha video bu mavzuni ravshanlashtirishi kerak, chunki biz struct, enum, class va shu kabi yaxshi narsalarga chuqur kirib boramiz.

Tomosha qilganingiz uchun rahmat! Yana bir bor aytaman: agar bu hozircha to'liq tushunarli bo'lmasa, xavotir olmang β€” bu juda intensiv mavzu, va dasturchilik yo'lingizning shu bosqichida buni to'liq chuqur tushunib olishingiz shart emas. Agar siz Swift-ni endi o'rganayotgan bo'lsangiz, men sizni shu mavzu bilan shunchaki tanishtirmoqchi edim, toki biz bu haqida o'ylab boramiz, va keyinroq bunga haqiqatan ehtiyoj tug'ilganda, siz shu joyga qaytib kelib, buni haqiqatan tushunib olasiz. Hozircha esa, buni shunchaki yuqori darajada qabul qiling va farqlar mavjudligini tushunib qo'ying β€” keyingi bir necha videoda struct va class bilan qanday ishlashni, va ularning farqlarini ko'rsataman.

Tomosha qilganingiz uchun rahmat! Men β€” Nick, bu Swiftful Thinking, keyingi videoda ko'rishamiz!

Buy mea coffee