- Published on
Swift-da Optional turlardan qanday foydalanish kerak
- Authors
- Name
- ShoxruxC
- @iOSdasturchi
Hammaga yana xush kelibsiz! Bilmadim, siz tayyormisiz, ammo baribir shunga sho'ng'ishimiz kerak — bu, menimcha, ushbu pleylistdagi haqiqatan ham qiyin kod yoziladigan birinchi video. Sekin-sekin tushuntirishga harakat qilaman, ammo bularni o'rganishimiz shart. Swift-da optional deb ataladigan narsa mavjud, va optional — bu qiymatga ega bo'lishi mumkin yoki mumkin emas bo'lgan ma'lumot bo'lagi.
Oldinroq biz string va boolean kabi oddiy turlarni ko'rib chiqgan edik, endi esa optional string va optional boolean larni ko'rib chiqamiz. Eng sodda misol qilib aytsak: oddiy Boolean — true yoki false qiymatga ega. Optional Boolean esa true, false yoki nil (ya'ni qiymat yo'q) bo'lishi mumkin.
Demak, biror narsa optional bo'lsa, bu degani — unda qiymat bo'lishi mumkin, lekin bo'lmasligi ham mumkin. Agar hech qachon kod yozmagan bo'lsangiz, bu g'alati tuyulishi mumkin, ammo haqiqatda ilova yozayotganingizda, ba'zan sizda ma'lumot bor deb o'ylaysiz, lekin dastur ishlayotgan paytda (runtime-da) bu ma'lumot bo'lmasligi ham mumkin. Masalan, ilovangiz ma'lumotni yuklayotgan bo'lsa-yu, hali ma'lumot kelmagan bo'lsa — aynan shu holatlarda optionallardan foydalanamiz.
Ushbu videoda optional nima ekanligini, undan qanday foydalanishni, shuningdek optionallarni xavfsiz tarzda unwrap (ochish) qilishni o'rgatadigan bir nechta funksiyalar yozamiz.
"Unwrap" atamasini ko'p eshitasiz — bu optional qiymatni olib, uni endi optional bo'lmagan holga keltirish degani. Buning xavfsiz va xavfli usullari mavjud, va xavfsiz usullarni o'rganish — Swift va iOS-ni o'rganishdagi eng muhim narsalardan biridir. Optionallarni qanday unwrap qilishingiz — sizning qanchalik yaxshi Swift dasturchisi ekanligingizning aniq belgisidir. Optionallarni doim xavfsiz tarzda unwrap qiling — aks holda, noto'g'ri usulda qilsangiz, ilovangiz qulab tushishi (crash) mumkin.
Optional nima
Yangi playground sahifa yaratamiz va unga Optionals deb nom beramiz. Avval oddiy Boolean yarataylik:
let myBool: Bool = false
print(myBool)
Bu — har doim true yoki false bo'ladigan oddiy Boolean. Ammo agar turning yoniga savol belgisi (?) qo'ysak, bu optional Boolean ekanligini bildiradi:
let myOtherBool: Bool? = false
print(myOtherBool)
Bool— har doim qiymatga ega:trueyokifalse.Bool?— qiymatga ega bo'lishi mumkin (trueyokifalse), yoki umuman qiymatga ega bo'lmasligi mumkin (nil).
Swift-da optionallardan juda ko'p foydalanamiz — bu dasturchining eng yaqin do'sti, chunki ko'pincha siz ma'lumot bilan ishlayapsiz, lekin bu ma'lumot albatta bo'ladimi yoki yo'qmi — bunga ishonchingiz komil emas. Shu sababli optional yaratamiz, va kodimizda bu qiymat bor-yo'qligini tekshirishni o'rganishimiz kerak bo'ladi.
var myOtherBool: Bool? = nil
print(myOtherBool)
myOtherBool = true
print(myOtherBool)
myOtherBool = false
print(myOtherBool)
myOtherBool = nil
print(myOtherBool)
Kodni ishga tushirsak, qiymat avval nil, so'ngra true, keyin false, va yana nil bo'lib o'zgarib borishini ko'ramiz. Optional qiymatni chop etganimizda, Xcode konsolda buni "optional" ekanligini ko'rsatadi va kompilyator bizga ogohlantirish (warning) beradi — chunki biz optional qiymatni to'g'ridan-to'g'ri chop etmoqdamiz.
Nil coalescing operatori (??)
Yuqoridagi ogohlantirishni yo'qotishning bir usuli — bu qiymatga standart (default) qiymat berishdir. Buning uchun qo'sh savol belgisi (??) ishlatiladi — bu nil coalescing operatori deb ataladi, va u oddiy qilib "aks holda" degan ma'noni bildiradi.
Avval, optional qiymatni to'g'ridan-to'g'ri optional bo'lmagan o'zgaruvchiga berishga urinib ko'raylik:
let myOtherBool: Bool? = nil
let newValueTwo: Bool = myOtherBool
// Xatolik: Value of optional type 'Bool?' must be unwrapped to a value of type 'Bool'
Kompilyator bizni to'xtatadi, chunki optional qiymatni optional bo'lmagan qiymatga to'g'ridan-to'g'ri berib bo'lmaydi. Buni tuzatishning bir yo'li — ?? orqali standart qiymat berish:
var myOtherBool: Bool? = nil
let newValueTwo: Bool = myOtherBool ?? false
print("New value 2: \\(newValueTwo)")
Bu shuni bildiradi: newValueTwo — myOtherBoolning qiymatiga teng, agar u mavjud bo'lsa; aks holda u falsega teng bo'ladi. Demak, kod avval shu yerdan qiymatni olishga harakat qiladi, agar qiymat nil bo'lsa — falseni standart qiymat sifatida oladi, aks holda haqiqiy qiymatni ishlatadi.
Kodni ishga tushirsak — myOtherBool hozir nil, shuning uchun standart qiymat false chop etiladi. Agar uni truega o'zgartirsak:
var myOtherBool: Bool? = true
let newValueTwo: Bool = myOtherBool ?? false
print("New value 2: \\(newValueTwo)")
Endi true chiqadi, chunki bu safar qiymat mavjud.
Nil coalescing-ning afzalligi shunda: endi newValueTwodan kodimizda optional bo'lmagan holatda foydalanishimiz mumkin — chunki optional qiymatni kod bo'ylab tashib yurganimizda, har bir funksiyada "bu yerda haqiqatan qiymat bormi" deb tekshirishimiz va ikkala holatni (bor/yo'q) qayta ishlashimiz kerak bo'ladi. Optional bo'lmagan qiymatga ega bo'lgach esa, bu doim true yoki false bo'lishini aniq bilamiz.
Istalgan tur optional bo'lishi mumkin
Optional faqat Boolean uchun emas — istalgan tur optional bo'la oladi, shunchaki uning yoniga savol belgisi qo'yamiz:
var myString: String? = "Hello world"
print("My string: \\(myString ?? "there is no value")")
myString = "Some new text"
print("My string: \\(myString ?? "there is no value")")
myString = nil
print("My string: \\(myString ?? "there is no value")")
Kodni ishga tushirsak: avval "Hello world", so'ngra "Some new text", va nihoyat myString nil bo'lganda — bizning standart qiymatimiz, ya'ni "there is no value" chop etiladi.
Optionallarni xavfsiz unwrap qilishning uch usuli
Swift-da optionalni xavfsiz unwrap qilishning uch usuli mavjud: nil coalescing, if let va guard let. Keling, bularning har birini ko'rib chiqaylik.
Avval bir misol yarataylik:
var userIsPremium: Bool? = nil
func checkIfUserIsPremium() -> Bool? {
return userIsPremium
}
let isPremium = checkIfUserIsPremium()
print(isPremium)
Bu funksiya optional Boolean qaytaradi, va shuning uchun bizning ilovamizda hamon optional qiymat bilan ishlashimiz kerak bo'ladi.
1. Nil coalescing bilan
Buning o'rniga, funksiyaning o'zi optional bo'lmagan qiymat qaytarishini ta'minlashimiz mumkin:
var userIsPremium: Bool? = nil
func checkIfUserIsPremiumTwo() -> Bool {
return userIsPremium ?? false
}
let isPremium = checkIfUserIsPremiumTwo()
print(isPremium)
Endi ilovamizda haqiqiy Bool bor, optional emas — biz har doim aniq true yoki false qiymatga ega bo'lamiz, hech qachon nil holatiga tushmaymiz.
2. if let bilan
Endi if let orqali qanday unwrap qilishni ko'ramiz:
var userIsPremium: Bool? = nil
func checkIfUserIsPremiumThree() -> Bool {
if let newValue = userIsPremium {
return newValue
} else {
return false
}
}
print(checkIfUserIsPremiumThree())
Bu yerda aytilayotgan narsa: agar userIsPremiumda qiymat bo'lsa, newValue nomli yangi konstanta yarataylik va unga shu qiymatni beraylik. Demak, userIsPremium — optional Bool, ammo agar unda qiymat bo'lsa (ya'ni nil bo'lmasa), newValue endi optional bo'lmagan haqiqiy Boolean bo'ladi, va biz uni closure ichida ishlatishimiz mumkin.
Bu yerda shuni tushunish muhim: bu shart faqat qiymatning mavjud ekanligini tekshiradi —
trueyokifalseekanligini emas. U faqatnilemasligini bildiradi.
else qismi esa — agar qiymat mavjud bo'lmasa, ya'ni nil bo'lsa, ishga tushadi.
Swift-ning yangi sintaksisida, yangi nom o'ylab topishimiz shart emas — biz xuddi shu nomdan foydalanishimiz mumkin:
var userIsPremium: Bool? = nil
func checkIfUserIsPremiumFour() -> Bool {
if let userIsPremium {
return userIsPremium
}
return false
}
print(checkIfUserIsPremiumFour())
Bu yerda if let userIsPremium — agar tashqaridagi userIsPremium optional o'zgaruvchisida qiymat bo'lsa, closure ichida xuddi shu nom bilan, lekin optional bo'lmagan yangi konstanta yaratiladi. Closure ichida userIsPremiumga murojaat qilganimizda, bu allaqachon faylning yuqorisidagi optional versiyasi emas, balki shu yerda yaratilgan optional bo'lmagan versiyaga ishora qiladi.
3. guard let bilan
Buni guard let orqali ham yozish mumkin:
var userIsPremium: Bool? = nil
func checkIfUserIsPremiumSix() -> Bool {
guard let newValue = userIsPremium else {
return false
}
return newValue
}
print(checkIfUserIsPremiumSix())
Va xuddi shunday, qisqartirilgan shaklda:
var userIsPremium: Bool? = nil
func checkIfUserIsPremiumSeven() -> Bool {
guard let userIsPremium else {
return false
}
return userIsPremium
}
print(checkIfUserIsPremiumSeven())
if let va guard let orasidagi farq
if let— agar qiymat mavjud bo'lsa, closure ichiga kiramiz.guard let— qiymat mavjudligiga ishonch hosil qilamiz; agar qiymat mavjud bo'lmasa,elseblokiga kirib, funksiyadan erta chiqib ketamiz.
Demak, if letda — muvaffaqiyat closure-ga olib boradi. guard letda esa — muvaffaqiyatsizlik closure-ga (else blokiga) olib boradi, muvaffaqiyat esa kodni pastga davom ettirishni anglatadi.
Bir nechta optionalni birga unwrap qilish
Endi murakkabroq misolga o'taylik. Aytaylik, bizda quyidagi uchta optional o'zgaruvchi bor:
var userIsNew: Bool? = true
var userDidCompleteOnboarding: Bool? = false
var userFavoriteMovie: String? = nil
print(userIsNew, userDidCompleteOnboarding, userFavoriteMovie)
Va bizda shu uchta qiymatni (optional bo'lmagan holda) talab qiluvchi funksiya bor:
func getUserStatus(isNew: Bool, didCompleteOnboarding: Bool, favoriteMovie: String) -> Bool {
if isNew && didCompleteOnboarding {
return true
} else {
return false
}
}
Agar shu funksiyaga to'g'ridan-to'g'ri optional o'zgaruvchilarimizni uzatishga urinsak, kompilyator xatolik beradi:
let result = getUserStatus(
isNew: userIsNew,
didCompleteOnboarding: userDidCompleteOnboarding,
favoriteMovie: userFavoriteMovie
)
// Xatolik: Value of optional type 'Bool?' must be unwrapped
// to a value of type 'Bool'
Buni tuzatish uchun, avval barcha uchta qiymatni unwrap qilishimiz kerak.
if let bilan — barchasini birga tekshirish
Bir nechta if letni vergul bilan zanjirlab, hammasini bir vaqtning o'zida tekshirishimiz mumkin:
var userDidCompleteOnboarding: Bool? = false
var userFavoriteMovie: String? = "Inception"
func getUserStatus(isNew: Bool, didCompleteOnboarding: Bool, favoriteMovie: String) -> Bool {
if isNew && didCompleteOnboarding {
return true
} else {
return false
}
}
func checkIfUserIsSetUp() -> Bool {
if let userIsNew, let userDidCompleteOnboarding, let userFavoriteMovie {
return getUserStatus(
isNew: userIsNew,
didCompleteOnboarding: userDidCompleteOnboarding,
favoriteMovie: userFavoriteMovie
)
} else {
return false
}
}
print(checkIfUserIsSetUp())
Bu yerda aytilayotgan narsa: agar uchtasining hammasida qiymat bo'lsa — userIsNew nil emas, userDidCompleteOnboarding nil emas, va userFavoriteMovie nil emas bo'lsa — closure ichiga kiramiz, va u yerda barcha uchta qiymat optional bo'lmagan holatda mavjud bo'ladi. Agar bu uchtasidan birortasi ham nil bo'lsa, biz else blokiga tushamiz.
guard let bilan — yanada soddaroq
Endi xuddi shu mantiqni guard let bilan yozaylik:
var userIsNew: Bool? = true
var userDidCompleteOnboarding: Bool? = false
var userFavoriteMovie: String? = "Inception"
func getUserStatus(isNew: Bool, didCompleteOnboarding: Bool, favoriteMovie: String) -> Bool {
if isNew && didCompleteOnboarding {
return true
} else {
return false
}
}
func checkIfUserIsSetUpTwo() -> Bool {
guard let userIsNew, let userDidCompleteOnboarding, let userFavoriteMovie else {
return false
}
return getUserStatus(
isNew: userIsNew,
didCompleteOnboarding: userDidCompleteOnboarding,
favoriteMovie: userFavoriteMovie
)
}
print(checkIfUserIsSetUpTwo())
Bu yerda farq shunda: agar uchta qiymatdan birortasi nil bo'lsa, biz darrov else blokiga tushib, funksiyadan false qaytarib chiqib ketamiz. Agar barchasi mavjud bo'lsa, funksiya closure ichiga kirmasdan, to'g'ridan-to'g'ri davom etadi va shu uchta qiymatdan foydalanadi.
"Qatlamlangan" (layered) if let muammosi
Ba'zan har uchta qiymatni bir vaqtning o'zida emas, balki alohida-alohida tekshirish kerak bo'ladi — masalan, agar bittasi mavjud bo'lsa bir narsa qilamiz, ikkitasi mavjud bo'lsa boshqa narsa, uchtasi mavjud bo'lsa esa yana boshqa narsa qilamiz. Buni if letlar bilan ichma-ich (nested) yozish mumkin:
var userIsNew: Bool? = true
var userDidCompleteOnboarding: Bool? = true
var userFavoriteMovie: String? = "Inception"
func checkIfUserIsSetUpThree() -> Bool {
if let userIsNew {
if let userDidCompleteOnboarding {
if let userFavoriteMovie {
return true
} else {
return false
}
} else {
return false
}
} else {
return false
}
}
print(checkIfUserIsSetUpThree())
Bu kod to'liq ishlaydi, hech qanday xato yo'q. Ammo ko'rinib turibdiki, murakkab kod yozishni boshlaganimizda, bu juda chalkash bo'lib ketadi — chunki har bir qatlamda qaysi closure ichida ekanligimizni kuzatib borishimiz kerak bo'ladi. Bu hali ham nisbatan sodda misol, ammo haqiqiy murakkab ilovada, ko'plab murakkab ma'lumotlar bilan, closure ichida closure, ichida yana closure yozish — sizga ham, boshqa dasturchilarga ham juda chalkash bo'lib qoladi. Iloji bo'lsa, bunday yondashuvdan qochishimiz kerak.
"Qatlamlangan" guard — ancha soddaroq
Buning o'rniga, xuddi shu mantiqni guard let bilan yozsak:
var userIsNew: Bool? = true
var userDidCompleteOnboarding: Bool? = true
var userFavoriteMovie: String? = "Inception"
func checkIfUserIsSetUpFour() -> Bool {
guard let userIsNew else {
return false
}
guard let userDidCompleteOnboarding else {
return false
}
guard let userFavoriteMovie else {
return false
}
return true
}
print(checkIfUserIsSetUpFour())
Bu yerdagi farqni payqadingizmi? getUserStatusni chaqirganimizda, biz hech qanday closure ichida emasmiz — funksiyaning asosiy darajasida turamiz, faqat har bir tekshirishni alohida bajarib o'tamiz xolos. Bu kod, izohlarsiz va keraksiz qismlarsiz yozilganda, oldingisidan ancha sodda va o'qilishi oson.
Agar
if letyokiguard letdan birortasini ishlatish imkoni bo'lsa, ko'pchilik holatlarda **guard let**ni afzal ko'rish tavsiya etiladi — chunki bu closure ichiga kirishni talab qilmaydi, va closure-lar chuqurlashib ketishi bilan kod tezda murakkablashadi. Ammo bu — shaxsiy did masalasi ham, va har doim ham aynan to'g'ri javob emas.
Optional chaining (optionallarni zanjirlash)
Endi Swift-ning yana bir qiziqarli xususiyati — optional chainingni ko'rib chiqamiz.
func getUsername() -> String? {
return "test"
}
func getTitle() -> String {
return "title"
}
let username: String? = getUsername()
let characterCount = username?.count
let title: String = getTitle()
let countTwo = title.count
print(characterCount, countTwo)
usernamening turi optional String, shuning uchun username?.count yozganimizda, savol belgisi paydo bo'ladi — bu bizga, dasturchiga, shuni bildiradi: bu qiymat ham optional. Demak, characterCountni faqat username nil bo'lmagan holatda olamiz; aks holda, u ham nil bo'lib qoladi.
titleesa — optional bo'lmagan oddiy String, shuning uchun title.count har doim mavjud bo'ladi, va countTwo optional emas.
Buni unwrap qilishning afzalligi shunda — biz buni if let, guard let yoki nil coalescing orqali, oddiy bir qiymatdagi kabi davom ettirishimiz mumkin:
func getUsername() -> String? {
return "test"
}
let username: String? = getUsername()
if let count = username?.count {
print("Character count: \\(count)")
} else {
print("No username")
}
Bir nechta optionalni zanjirlash
Optionallarni bir-biriga zanjir qilib ham ulash mumkin. Masalan, foydalanuvchi nomining birinchi harfi kichik harf ekanligini tekshirmoqchi bo'laylik:
func getUsername() -> String? {
return "test"
}
let username: String? = getUsername()
let firstCharacterIsLowercased = username?.first?.isLowercased()
print(firstCharacterIsLowercased ?? false)
Bu yerda ikkita savol belgisini ko'rasiz: username? va .first?. Bu shuni bildiradi: agar usernameda qiymat bo'lsa va uning ichida birinchi belgi mavjud bo'lsa (nil emas), faqat shundagina isLowercased() funksiyasi ishga tushadi. Agar username bo'sh satr ("") bo'lsa, masalan, username o'zi nil bo'lmaydi, ammo uning ichida birinchi belgi bo'lmaydi — shuning uchun first nil bo'ladi.
Bu — optional chaining deb ataladi, ya'ni optionallarni bir-biriga zanjirlab ulash. Hozircha bu biroz keraksiz tuyulishi mumkin, ammo murakkab funksiyalar yoza boshlaganingizda, ko'plab narsalarni shu tarzda zanjirlashga to'g'ri keladi — bu bizga ko'plab if let va guard bayonotlarini yozishdan qutqaradi, chunki hammasini bir qatorga sig'dirib qo'yamiz. Bu natija ham hamon optional bo'lgani uchun, butun zanjirga ham ?? orqali standart qiymat berishimiz mumkin — bu Swift dasturchilariga optional qiymatlar bilan ishlashda yordam beradigan yana bir usul.
Xavfli (explicit) unwrapping — undov belgisi (!)
Va nihoyat, optionallarni unwrap qilishning xavfsiz bo'lmagan usuli ham mavjud — bu explicit unwrapping, ya'ni undov belgisi (!) orqali amalga oshiriladi.
func getUsername() -> String? {
return nil
}
let username: String? = getUsername()
let countThree: Int = username!.count
// Bu qator kompilyatsiya bo'ladi, AMMO agar "username" nil bo'lsa,
// ilova RUNTIME-da QULAB TUSHADI (crash):
// Fatal error: Unexpectedly found nil while unwrapping an Optional value
Bu yerda dasturchi kompilyatorga shunday demoqda: "Men username optional ekanligini bilaman, ammo unda albatta qiymat bo'lishiga 100 foiz ishonaman, shuning uchun uni majburan ochaman." Muammo shundaki, agar username haqiqatan ham nil bo'lsa, bu qator ilovangizni butunlay qulatib yuboradi.
Hech qachon undov belgisi (
!) orqali optionalni "majburan" ochmang, agar siz bu qiymatning 100 foiznilemasligiga to'liq ishonchingiz komil bo'lmasa — masalan, agar o'zgaruvchining o'zi optional bo'lmasa, yoki siz uni allaqachon boshqa joyda xavfsiz unwrap qilib bo'lgan bo'lsangiz. Agar qiymat hali optional bo'lib, hali unwrap qilinmagan bo'lsa, bu usulni ishlatmang.
Bu — anti-pattern (yomon amaliyot namunasi) hisoblanadi, chunki kompilyatorning o'zi sizga bu qiymat optional ekanligini va xavfsiz unwrap qilishni so'rab turgan bo'lsa-da, dasturchi uni majburlab "men bilaman" deb o'tib ketadi. Agar siz biror iOS suhbatda (interview) shu usulni ishlatsangiz, bu odatda Swift tilini to'liq tushunmaganlik belgisi sifatida qaraladi.
Xulosa
Bu — juda murakkab video bo'ldi, ammo optionallarni tushunish va ularni xavfsiz unwrap qilishni bilish — Swift-ni o'rganishda eng muhim narsalardan biridir. Har qanday iOS intervyusida sizdan optionallar haqida so'raladi, va optionallarni qanday xavfsiz unwrap qilishingiz — Swift kodini qanchalik erkin yozishingizni ko'rsatadigan asosiy belgilardan biri hisoblanadi.
Ushbu videoda ko'rganlarimiz:
- Optional (
?) — qiymatga ega bo'lishi mumkin yoki bo'lmasligi mumkin bo'lgan ma'lumot nil— qiymat yo'qligini bildiradi- Nil coalescing (
??) — "aks holda" standart qiymat berish if let— agar qiymat mavjud bo'lsa, closure ichiga kirishguard let— qiymat mavjudligiga ishonch hosil qilish, aks holda funksiyadan erta chiqish- Optional chaining (
?.) — bir nechta optionalni zanjirlab, qisqa yo'l bilan tekshirish - Explicit unwrapping (
!) — xavfli usul, faqat 100 foiz ishonch bo'lgandagina ishlatiladi
Optionallarni xavfsiz unwrap qilishni o'zlashtirib olganingizdan so'ng, sizning Swift dasturchisi sifatidagi darajangiz sezilarli darajada oshadi. Tomosha qilganingiz uchun rahmat! Men — Nick, bu Swiftful Thinking, keyingi videoda ko'rishamiz!