Published on

Swift-da funksiyalardan qanday foydalanish kerak

Authors

Hammaga yana xush kelibsiz! Oldingi video, menimcha, ancha qiyin bo'ldi — biz juda ko'p mantiqiy operatorlar bilan ishladik, va bu sizlar uchun ehtimol yangi narsa edi. Biz bu mantiqning hammasini doim ishlatavermaymiz, ammo uni asboblar qutimizda saqlab yurish kerak, chunki u ko'p marta foydaga keladi.

Shuni payqagan bo'lsangiz kerak: oldingi videoda yozgan barcha kodimiz asosan bitta katta faylda edi. Bu playground-da, ya'ni kod yozishni o'rganayotganimizda mutlaqo to'g'ri yondashuv. Ammo agar biz haqiqiy ilova yaratishni boshlasak, hech qachon butun kodimizni bitta faylga tashlab qo'ymaymiz. Buning o'rniga, biz kodimizdan chaqirib foydalanishimiz mumkin bo'lgan funksiyalarga ega bo'lgan maxsus fayllar yaratamiz.

Ilovangiz ishga tushganda, aslida nima bo'ladi? Bitta funksiya bir bo'lak kodni bajaradi, so'ngra bu funksiya yana bir bo'lak kodni bajaruvchi boshqa funksiyani chaqiradi — va butun ilovangiz aslida bir-birini chaqirib turadigan funksiyalar zanjiridan iborat. Demak, bitta katta fayl o'rniga, biz kodimizning kichik bo'laklarini bajaradigan ko'plab funksiyalar yaratamiz, va har bir funksiya o'zining maqsadiga ega bo'ladi — bir funksiya bir narsani, boshqasi esa boshqa narsani bajaradi. Shu tarzda kodimizni alohida bo'laklarga ajratib turamiz.

Ushbu videoda funksiyalarni qanday yozish sintaksisini o'rganamiz. Bu, menimcha, juda murakkab emas, ammo ko'plab yangi sintaksis bo'lgani uchun bu alohida videoga loyiq. Keyingi bir necha videoda biz juda ko'p funksiya yozamiz, shuning uchun bularning hammasini bir zumda to'liq tushunib olishingiz shart emas — bu shunchaki kirish, va keyinroq shu bilimimiz ustiga qurib boramiz.


Yangi playground sahifa

Navigator-da o'ng tugmani bosib, yangi playground sahifa yaratamiz va unga Functions deb nom beramiz. import Foundationni qoldiramiz va kod yozishni boshlaymiz.

Hozirgacha biz kodni shunga o'xshash tarzda yozib kelgan edik:

import Foundation

var myItem: Bool = false
myItem = true
print(myItem)

Playground uchun bu mutlaqo yaxshi, va biz aynan shu tarzda kod yozib keldik. Ammo haqiqatda ilova bunday ishlamaydi — haqiqiy ilovada kodingiz har bir fayl ichida funksiyalarga bo'linadi. Faylning o'zida ham bir nechta obyekt va funksiya bo'ladi, va biz mantiqni aynan shu funksiyalar ichiga yozamiz. Shuning uchun endi kodimizni faylga to'g'ridan-to'g'ri yozish o'rniga, uni funksiyalarga joylashtirishni boshlaymiz.


Birinchi funksiyani yaratish

Funksiya yaratish uchun func kalit so'zini yozamiz, so'ngra funksiyaga nom beramiz, qavslarni ochib-yopamiz va keyin qavs ichiga ({ }) o'tamiz:

func myFirstFunction() {
    print("My first function called")
}

Bu qavslar ham — closure (yopiq blok), xuddi if shartlarimizda bo'lgani kabi. Ammo bu closure faqat biz funksiyani chaqirganimizda bajariladi. Agar shu kodni ishga tushirsak, hech narsa chiqmaydi, chunki biz hali bu funksiyani ilovamizdan hech qachon chaqirmaganmiz.

Funksiyani chaqirish uchun, uning nomini yozib, qavslarni ochib-yopamiz:

func myFirstFunction() {
    print("My first function called")
}

myFirstFunction()

Endi funksiya ichidagi kod ishga tushadi va konsolga chop etiladi. Ko'pchilik ilovalar aynan shu tarzda ishlaydi: tizim qaerdandir bitta boshlang'ich funksiyani yaratadi, so'ngra siz unga "ilova ishga tushganda, avval shu faylga o'tib, ekranda buni ko'rsat" deb aytasiz, va shu fayl esa o'z navbatida ko'plab boshqa funksiyalarni chaqiradi — "shu ma'lumotni yukla", "shu tugmani ko'rsat" va shunga o'xshash. Demak, butun ilovangiz, aslida, bir funksiyadan boshlanadigan funksiyalar zanjiridan iborat.


Funksiyalarni zanjirlash

Keling, yana bir funksiya yaratamiz:

func myFirstFunction() {
    print("My first function called")
}

func mySecondFunction() {
print("My second function called")
}

myFirstFunction()
mySecondFunction()

Bu ikkala funksiya ham ishga tushadi. Ammo biz bu funksiyalarni zanjir qilib ham yozishimiz mumkin — masalan, myFirstFunction bajarilganda, u o'zi mySecondFunctionni chaqirsin deb xohlasak, shu chaqiruvni myFirstFunctionning ichiga joylashtiramiz:

func myFirstFunction() {
    print("My first function called")
    mySecondFunction()
}

func mySecondFunction() {
print("My second function called")
}

myFirstFunction()

Natija tashqi ko'rinishidan bir xilday tuyulsa-da, bu safar kod biroz boshqacha ishlaydi: myFirstFunction chaqirilganda, u o'z ichidagi qatorlarni birma-bir bajaradi, va shu jarayonda mySecondFunctionni ham chaqiradi. Buni davom ettirishimiz ham mumkin — masalan, uchinchi funksiya qo'shib, mySecondFunction uni chaqirishi mumkin, yoki hatto myFirstFunctionning o'zi ham uchinchi funksiyani chaqira oladi. Funksiyalarni shunday zanjir qilib, bir-birini chaqirtirib turish — har qanday dasturiy ta'minot yozishda mutlaqo oddiy holat hisoblanadi.


Funksiya ichidagi ma'lumotlar maxfiy bo'ladi

Endi biroz murakkabroq misolga o'taylik:

func getUsername() {
    let userName = "Nick"
    print(userName)
}

getUsername()

Bu yerda muhim narsani ta'kidlab o'tmoqchiman: userName konstantasi closure ichida yaratilgan. Demak, agar men shu closure-dan tashqarida bo'lsam, men userNamega murojaat qila olmayman — kompilyator bu nima ekanligini bilmaydi, chunki bu ma'lumot faqat shu closure-ga maxfiy (private). Agar men shu qiymatni closure-dan tashqariga chiqarib qo'ysam, u endi shu funksiyaga emas, balki butun faylga ochiq (public) bo'lib qoladi.

Swift-da, va umuman har qanday dasturlash tilida, biz imkon qadar narsalarni maxfiy saqlashimiz kerak. Agar biror qiymat faqat bitta funksiyada kerak bo'lsa, uni shu funksiya ichida saqlash yaxshi odat hisoblanadi.


Funksiyadan qiymat qaytarish

Hozircha biz getUsername funksiyasini chaqirib, natijani shunchaki konsolga chop etib qo'yardik. Ammo agar men ushbu qiymatni funksiyadan qaytarib olib, uni boshqa joyda ishlatmoqchi bo'lsam-chi? Buning sintaksisi — funksiya deklaratsiyasidan keyin o'q belgisi (->) qo'yib, qaytariladigan ma'lumot turini ko'rsatish:

func getUsername() -> String {
    let userName = "Nick"
    return userName
}

let name = getUsername()
print(name)

Endi kompilyator shuni biladi: closure oxirida albatta string qaytaruvchi return bayonoti bo'lishi kerak. Funksiyani chaqirganimizda, u bizga String turidagi qiymatni qaytaradi, va biz buni name konstantasiga saqlab olamiz. namening turi ham, albatta, String bo'ladi — chunki funksiyadan qaytgan qiymat aynan shu turga tegishli.

Qaytariladigan turni mos kelmasligi (xatolik)

Bu yerda Swift yana type-safe ekanligini ko'rsatadi. Agar biz String qaytarishi kerak bo'lgan funksiyadan qaytgan qiymatni boshqa turdagi o'zgaruvchiga berishga urinsak, kompilyator xatolik beradi:

func checkIfUserIsPremium() -> Bool {
    return false
}

let name: String = checkIfUserIsPremium()
// Xatolik: Cannot convert value of type 'Bool' to specified type 'String'

Demak, Swift har doim turga xavfsiz, ammo bu yerda bu ayniqsa muhim — chunki kompilyator funksiyadan qaytgan qiymat bilan biz uni saqlamoqchi bo'lgan o'zgaruvchining turi mos kelishini talab qiladi.


Funksiyaga parametr (qiymat) uzatish

Endi yana bir misol ko'raylik. Aytaylik, bizda quyidagi ikkita o'zgaruvchi bor:

var userDidCompleteOnboarding: Bool = false
var userProfileIsCreated: Bool = true

func checkUserStatus() -> Bool {
if userDidCompleteOnboarding && userProfileIsCreated {
return true
} else {
return false
}
}

let status = checkUserStatus()
print(status)

Oldingi videoda o'rgangan barcha operatorlarimiz — if, AND, OR — aynan mana shu funksiyalar ichida ishlatiladi, va biz hisoblangan natijani funksiyadan qaytaramiz.

Ammo, men avval ta'kidlaganimdek, haqiqiy ilovada faylda shunchaki "muallaq" o'zgaruvchilar bo'lishi kamdan-kam holat. Texnik jihatdan buni qilish mumkin, ammo bu global o'zgaruvchi deb ataladi va odatda yaxshi amaliyot hisoblanmaydi. Shuning uchun haqiqiy ilovada bu ma'lumot biror obyekt ichida yoki boshqa funksiya ichida bo'ladi.

Masalan, agar bizda showFirstScreen nomli funksiya bo'lsa, va u o'z navbatida checkUserStatusni chaqirmoqchi bo'lsa — muammo shundaki, closure ichida userDidCompleteOnboardingga murojaat qilinganda, kompilyator bu qiymat qayerdan kelayotganini bilmaydi. Aksariyat haqiqiy ilovalarda kod aynan shunday tuzilgan bo'ladi: sizda kod bor, va siz unga ma'lumot qaerdan kelayotganini aniq ko'rsatishingiz kerak. Buning yo'li — funksiyaga qiymatlarni uzatish.

Parametrlarni qo'shish

Funksiyaning qavslari ichi — bu funksiyaning initializatori, va biz shu joyga funksiya ichida ishlatadigan ma'lumotlarni uzatishimiz mumkin:

func checkUserStatus(didCompleteOnboarding: Bool, profileIsCreated: Bool) -> Bool {
    if didCompleteOnboarding && profileIsCreated {
        return true
    } else {
        return false
    }
}

let status = checkUserStatus(didCompleteOnboarding: false, profileIsCreated: true)

if status {
print("Show home screen")
} else {
print("Show onboarding screen")
}

Endi checkUserStatus funksiyasini chaqirganimizda, Xcode bizdan albatta didCompleteOnboarding va profileIsCreated qiymatlarini uzatishni talab qiladi — bu funksiyani boshqa hech qanday usulda chaqirib bo'lmaydi. Kodni ishga tushirsak, "Show onboarding screen" chop etiladi, chunki foydalanuvchi onboarding-ni tugallamagan.

Bu yerda men sizlarga ma'lumotni bir funksiyadan ikkinchisiga, initializatorni yangilash orqali qanday uzatishni ko'rsatmoqchi edim. Bu qiymatlar Boolean bo'lishi shart emas — biz string, raqam yoki istalgan boshqa turni ham uzatishimiz mumkin, va hatto bu funksiyalarni zanjir qilib davom ettirishimiz mumkin — masalan, bitta funksiya boshqa funksiyaga Boolean qiymat uzatadigan bo'lsa.

Kod o'rganishda eng chalkash narsalardan biri — ma'lumotni bir joydan ikkinchi joyga qanday yetkazish. Aytib qo'yay: bu sehr emas. Ma'lumotni ilovangiz bo'ylab qo'lda harakatlantirishingiz kerak bo'ladi. Agar ma'lumot ilovangizda harakatlanayotgan bo'lsa-yu, siz buni qanday qilib bo'layotganini tushunmasangiz, demak bu yozgan kodingizning tushunilmagan bir yon ta'siridir.

Kod yozayotganimizda biz doim qaysi ma'lumot qaysi funksiya va faylga maxfiy ekanligini, va bu ma'lumotni bir fayldan ikkinchisiga qanday yetkazishni boshqarib turamiz.


Funksiya ichidagi boshqaruv (control flow)

Void — hech narsa qaytarmaslik

E'tibor bering: yuqoridagi funksiyalarimizdan biri Boolean qaytargan, boshqa biri esa hech narsa qaytarmagan:

func doSomething() {
    print("Execute one")
}

doSomething()

Agar funksiya hech narsa qaytarmasa, bu Void qaytarish bilan bir xil. Kod yozganingizda ba'zan Void so'zini ko'rishingiz mumkin — Void shunchaki "hech narsa" degani: na true, na false, na string — funksiya shunchaki hech qanday qiymat qaytarmaydi. Ammo Swift kompilyatori shuni tushunadiki, agar biz hech narsa yozmasak, bu xuddi Void qaytarish bilan bir xil — shuning uchun buni alohida yozish shart emas.

Funksiya ichida if statement

Funksiya ichida if shartlardan ham foydalanishimiz mumkin:

func doSomething() {
    let isNew: Bool = false

    if isNew {
        print("New")
    } else {
        print("Not new")
    }

}

doSomething()

Bu funksiya hech narsa qaytarmagani uchun, biz elseni ham qoldirib ketishimiz mumkin edi:

func doSomething() {
    let isNew: Bool = false

    if isNew {
        print("New")
    }

}

doSomething()

Har bir holat qiymat qaytarishi shart

Ammo agar funksiyaga "albatta string qaytarishi kerak" deb belgilab qo'ysak, kompilyator endi bu closure ichidagi har qanday holatda ham string qaytarilishini talab qiladi:

func doSomething() -> String {
    let isNew: Bool = false

    if isNew {
        return "New"
    }
    // Xatolik: Missing return in a function expected to return 'String'
}

Bu yerda kompilyator bizga return yetishmayotganini aytadi, chunki agar isNew false bo'lsa, kod if blokiga kirmaydi va hech narsa qaytarilmaydi. Buni tuzatish uchun, har bir holat uchun qiymat qaytarishimiz kerak:

func doSomething() -> String {
    let isNew: Bool = false

    if isNew {
        return "New"
    }

    return "Not new"

}

let result = doSomething()
print(result)

Xuddi shunday, agar bizda title degan string bo'lsa, va uni "Avengers" bilan solishtirsak:

func doSomething() -> String {
    let title: String = "Avengers"

    if title == "Avengers" {
        return "Marvel"
    } else {
        return "Not Marvel"
    }

}

print(doSomething())

Endi kompilyator bu if/else barcha holatlarni qamrab olganini tushunadi — chunki har ikki tarmoqda ham return mavjud, kod hech qachon ikkalasidan tashqariga chiqib qolmaydi.

returndan keyingi kod bajarilmaydi

Yana bir muhim jihat: funksiyadan return bajarilgandan so'ng, undan keyingi barcha qatorlar ishga tushmaydi. return — bu funksiyani tugatish va natijani ilovaning qolgan qismiga qaytarish degani. Demak, agar return biror joyda ishga tushsa, undan keyin yozilgan hech qanday kod bajarilmaydi — funksiya shu yerda to'xtaydi va natija chaqirilgan joyga qaytariladi.


guard bayonoti

Endi yana bir muhim narsani ko'rsatib o'taman — guard bayonoti.

func doSomethingElse() {
    let title: String = "Avengers"

    guard title == "Avengers" else {
        print("Not Marvel")
        return
    }

    print("Marvel")

}

doSomethingElse()

guard shartining ma'nosi — "shartning rost ekanligiga ishonch hosil qil". Agar title "Avengers"ga teng bo'lsa, kod davom etadi va pastga o'tadi. Agar teng bo'lmasa, else blokiga kirib, funksiyadan return orqali chiqib ketamiz.

if va guard orasidagi farq

Bu ikki yondashuv ko'pincha bir xil maqsadga xizmat qiladi, ammo natijaning mantig'i teskari:

if — agar shart rost bo'lsa, closure ichiga kiramiz. guard — agar shart yolg'on bo'lsa, closure ichiga (odatda else blokiga) kirib, funksiyadan erta chiqib ketamiz.

Solishtirib ko'raylik — bir xil mantiqni ikki xil usulda yozamiz:

func checkIfTitleIsAvengers() -> Bool {
    let title: String = "Avengers"

    if title == "Avengers" {
        return true
    } else {
        return false
    }

}

func checkIfTitleIsAvengers2() -> Bool {
let title: String = "Avengers"

    guard title == "Avengers" else {
        return false
    }

    return true

}

print(checkIfTitleIsAvengers())
print(checkIfTitleIsAvengers2())

Ikkala funksiya ham aynan bir xil ishni bajaradi. Ikkisi ham ilovalarda juda keng tarqalgan, va qaysi birini tanlash — vaziyatga bog'liq. Agar bu hozircha biroz chalkash bo'lsa, xavotir olmang — keyingi videoda optionallarni o'rganganimizda, if va guard bayonotlarini yana ko'p marta, juda chuqur ko'rib chiqamiz, chunki bular aynan optionallar bilan ishlashda eng ko'p qo'llaniladigan joylardan biri.

Hozircha shuni tushunish kifoya: funksiya yaratganimizda, funksiya ichidagi ma'lumot shu funksiyaga maxfiy bo'ladi, va agar funksiyadan qiymat qaytarsak, bu qiymatni har bir holatda qaytarishimiz shart. Funksiyaning oqimini boshqarishning ikki asosiy yo'li — bu if va guard bayonotlari.


Calculated variable (hisoblanadigan o'zgaruvchi)

Va nihoyat, sizlarga oxirgi bir narsani ko'rsatib qo'ymoqchiman — calculated variable (hisoblanadigan o'zgaruvchi). Agar siz shu pleylistni kuzatib borayotgan bo'lsangiz, ehtimol hozircha buni o'zingiz yozmaysiz, ammo vaqt o'tishi bilan buni tez-tez ishlatadigan bo'lasiz. Bular oddiy o'rganish loyihalarida kamroq, lekin haqiqiy ilovalarda ko'proq uchraydi. Keling, bularni qisqacha ko'rib chiqaylik — aslida bular, mohiyatan, funksiyalardir.

let numberOne: Int = 5
let numberTwo: Int = 8

func calculateNumbers() -> Int {
return numberOne + numberTwo
}

let resultOne = calculateNumbers()
print(resultOne)

Bu kod mutlaqo to'g'ri ishlaydi. Ammo aytib o'tganimdek, faylda shunchaki "muallaq" ma'lumot bo'lishi kamdan-kam holat — ko'pincha biz bu funksiyani quyidagicha yozamiz, ya'ni qiymatlarni parametr sifatida uzatamiz:

func calculateNumbers(valueOne: Int, valueTwo: Int) -> Int {
    return valueOne + valueTwo
}

let resultTwo = calculateNumbers(valueOne: 5, valueTwo: 8)
print(resultTwo)

Ammo agar funksiyamizga hech qanday qiymat uzatish kerak bo'lmasa — masalan, birinchi misoldagidek — biz buni funksiya o'rniga calculated variable sifatida ham yozishimiz mumkin. Buning uchun, biz oddiy o'zgaruvchi yaratganimizdagi kabi var yozamiz, lekin uning qiymatini bevosita bermay, closure ichida hisoblaymiz:

let numberOne: Int = 5
let numberTwo: Int = 8

var calculatedNumber: Int {
return numberOne + numberTwo
}

print(calculatedNumber)
let numberOne: Int = 5
let numberTwo: Int = 8

var calculatedNumber: Int {
return numberOne + numberTwo
}

print(calculatedNumber)

Xuddi yuqoridagi funksiya Int qaytargani kabi, bu — Int turidagi o'zgaruvchi, faqat farqi shunda: bu raqam biz unga murojaat qilganimizda hisoblanadi. Closure ichida funksiyada bajaradigan har qanday ishni bajarishimiz mumkin — bu yerda ham numberOne + numberTwoni qaytarib turibmiz, va bu closure aslida yuqoridagi funksiyaning closure-i bilan aynan bir xil.

Agar funksiyangizga qiymat uzatish kerak bo'lsa — funksiya yozing. Agar qiymat uzatish kerak bo'lmasa — odatda calculated variable yozish soddaroq bo'ladi.

Ko'pchilikni chalg'itadigan narsa shuki, calculatedNumberga har safar murojaat qilganingizda, uning closure-i qaytadan hisoblanadi — xuddi funksiyani chaqirgandagidek. Demak, yuqoridagi ikki misol — funksiya chaqirish va calculated variable-ga murojaat qilish — aslida bir xil ishni bajaradi.

Calculated variable-ga qiymat uzatib bo'lmaydi — agar sizga qiymat uzatish kerak bo'lsa, buni faqat funksiya orqali amalga oshirishingiz mumkin. Albatta, hech narsa uzatilmaydigan funksiyani yozish ham mutlaqo yaxshi dasturlash hisoblanadi — men sizlarga shunchaki buni boshqacha, calculated variable shaklida ham yozish mumkinligini ko'rsatmoqchi edim.


Xulosa

Bu videoda ko'p narsa bor edi, ammo xavotir olmang — biz bu kodlar ustida qayta-qayta ishlayveramiz, va oxir-oqibat hammasi joyiga tushadi. Endi siz funksiyalarni bilasiz, va bu juda muhim — chunki haqiqiy ilova yaratishni boshlaganimizda, biz aynan funksiyalarni bir-biriga zanjirlab, dasturimizni shu tarzda quramiz. Funksiyalarsiz, biz juda cheklangan imkoniyatlarga ega bo'lib qolardik.

Ushbu videoda ko'rganlarimiz:

  • Funksiya yaratish va chaqirishfunc kalit so'zi
  • Funksiyalarni zanjirlash — bir funksiya ichidan boshqasini chaqirish
  • Qiymat qaytarish-> belgisi orqali
  • Parametr uzatish — funksiyaning initializatoriga qiymatlar qo'shish
  • Void — hech narsa qaytarmaslik
  • if va guard — funksiya oqimini boshqarishning ikki asosiy yo'li
  • Calculated variable — qiymat uzatish kerak bo'lmaganda funksiyaning soddaroq shakli

Keyingi videoda biz Swift-ning eng muhim va kuchli xususiyatlaridan biri — optionallarni o'rganamiz, va shundan so'ng if va guard bayonotlarini yana ham chuqurroq ko'rib chiqamiz. Agar hozir bu narsalar biroz chalkash bo'lsa, xavotir olmang — keyingi video juda ko'p misol va haqiqiy qo'llanish holatlari bilan to'la bo'ladi.

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

Buy mea coffee