Kirish Salom! Mening ismim Kiril Famin va men iOS dasturchisiman. Bugun men Swift-da initsializatorlar kabi oddiy mavzuni yaxshilab ko'rib chiqmoqchiman. Ko'rinib turgan soddaligiga qaramay, ba'zida ushbu mavzuni tushunmaslik, tafsilotlarni o'rganmasdan tezda tuzatmoqchi bo'lgan umidsizlikka olib keladigan xatolarga olib keladi. to'liq Ushbu maqolada biz initsializatorlar bilan bog'liq barcha narsalarni ko'rib chiqamiz, jumladan: Maxsus tuzilmani belgilashda strukturaning a'zolar bo'yicha ishga tushirgichini qanday saqlash kerak Nima uchun sinflarda initsializator yozish har doim ham kerak emas Nima uchun qo'ng'iroq qilish har doim ham belgilangan ishga tushirgichda talab qilinmaydi super.init Nima uchun qo'ng'iroq qilishdan oldin kichik sinfning barcha maydonlari to'ldirilishi kerak super.init Pastki sinflarda minimal bekor qilingan barcha ota-initsializatorlarga qanday kirish mumkin ishga tushirgich kerak bo'lganda required Nima uchun har doim parametrlarsiz chaqiriladi, lekin va bekor qilinadi UIView.init() init(frame:) init(coder:) ...va boshqalar. Ammo keling, buni bosqichma-bosqich ko'rib chiqaylik. Mundarija Asoslar Tuzilmalar A'zolar bo'yicha ishga tushirgich Ixtiyoriy vs var let A'zolar bo'yicha ishga tushirgichni saqlab qolish Sinflar Belgilangan ishga tushirgich Qulaylik ishga tushirgich Superklassning qulaylik ishga tushirgichini saqlab qolish Bekor qilish sonini minimallashtirish Kompilyator yordami boshlang'ich: generics, protokollar, , required Self() final parametrlarsiz UIView() Muhtaram eslatmalar Muvaffaqiyatsiz ishga tushirish Enums Xulosa Tegishli havolalar Asoslar Apple kompaniyasining qo'llanmasida (aytmoqchi, initsializatorlar uchun hayratlanarli darajada batafsil) shunday deyilgan: Swift dasturlash tili (6) - bu sinf, tuzilma yoki ro'yxatga olish namunasini foydalanish uchun tayyorlash jarayoni. Bu jarayon oʻsha namunadagi har bir saqlangan xususiyat uchun boshlangʻich qiymatni oʻrnatish va yangi namuna foydalanishga tayyor boʻlgunga qadar talab qilinadigan boshqa sozlash yoki ishga tushirishni oʻz ichiga oladi. Initializatsiya Siz ushbu ishga tushirish jarayonini ma'lum turdagi yangi namunani yaratish uchun chaqirilishi mumkin bo'lgan maxsus usullarga o'xshash belgilash orqali amalga oshirasiz. Objective-C initsializatorlaridan farqli o'laroq, Swift ishga tushirgichlari qiymat qaytarmaydi. Ularning asosiy roli yangi turdagi namunalarni birinchi marta ishlatishdan oldin to'g'ri ishga tushirishni ta'minlashdir. initsializatorlarni Xo'sh, men bu erda hech narsa qo'shishim shart emas deb o'ylayman. Tuzilmalar Keling, strukturani ishga tushirishni muhokama qilishdan boshlaylik. Bu juda oddiy, chunki meros yo'q, lekin siz hali ham bilishingiz kerak bo'lgan ba'zi qoidalar mavjud. A'zolar bo'yicha ishga tushirgich Keling, oddiy tuzilmani yozamiz: struct BankAccount { let amount: Double let isBlocked: Bool } let bankAccount = BankAccount(amount: 735, isBlocked: Bool) E'tibor bering, biz boshlang'ichni aniq e'lon qilmasdan strukturani ishga tushira oldik. Buning sababi, tuzilmalar kompilyator tomonidan yaratilgan oladi. Bu ishlaydi. a'zolar bo'yicha ishga tushirgichni faqat tuzilmalar uchun tanlab, uning qanday ko'rinishini ko'rishingiz mumkin: Refactor → Generate Memberwise initializer ni init(amount: Double, isBlocked: Bool) { self.amount = amount self.isBlocked = isBlocked } Imzodan hamma parametrlar uchun qiymatlarni bermaslik kompilyatsiya xatosiga olib kelishini tushunish oson: let bankAccount = BankAccount(amount: 735) // ❌ Missing argument for parameter 'isBlocked' in call Biroq, agar siz talab qilinadigan argumentlar sonini kamaytirmoqchi bo'lsangiz, maxsus ishga tushirgichni belgilashingiz mumkin: init(amount: Double, isBlocked: Bool = false) { self.amount = amount isBlocked = isBlocked } let bankAccount = BankAccount(amount: 735) // ✅ Esda tutingki, agar to'ldirilmagan bo'lsa, bu kompilyatsiya xatosiga olib keladi, chunki . isBlocked barcha struktura xususiyatlari initsializerda to'ldirilishi kerak vs Majburiy emas, var let Maydonni to'ldirish shart bo'lmagan yagona holat - bu ( ) ( ). Bunday hollarda maydon sukut bo'yicha ga aylanadi: aniq ixtiyoriy ? o'zgaruvchi var nil struct BankAccount { let amount: Double var isBlocked: Bool? init(amount: Double) { self.amount = amount } } let bankAccount = BankAccount(amount: 735) // ✅ Ammo, agar biz bu holatda a'zolar bo'yicha ishga tushirgichdan foydalanishga harakat qilsak, biz kompilyatsiya xatosini olamiz: let bankAccount = BankAccount( amount: 735, isBlocked: false ) // ❌ Extra argument 'isBlocked' in call A'zolar bo'yicha ishga tushirgichni saqlab qolish Buning sababi, maxsus ishga tushirgichni e'lon qilish a'zolar bo'yicha ishga tushirgichni olib tashlaydi. Uni aniq belgilash hali ham mumkin, lekin u avtomatik ravishda mavjud bo'lmaydi. Biroq, a'zolar bo'yicha ishga tushirgichni saqlab qolish uchun kichik hiyla bor: maxsus ishga tushirgichni e'lon qilish . extension struct BankAccount { let amount: Double var isBlocked: Bool? } extension BankAccount { init(amount: Double) { self.amount = amount } } let barclaysBankAccount = BankAccount(amount: 735) // ✅ let revolutBankAccount = BankAccount(amount: 812, isBlocked: false) // ✅ print(barclaysBankAccount.isBlocked) // nil print(barclaysBankAccount.isBlocked) // false Tuzilmalar uchun xulosa Barcha maydonlar ishga tushirgichda to'ldirilishi kerak Ixtiyoriy var maydonlari sukut bo'yicha nil Strukturalar a'zolar bo'yicha bepul ishga tushirgich oladi Agar maxsus ishga tushirgich e'lon qilingan bo'lsa, a'zolar bo'yicha ishga tushirgich yo'qoladi Strukturaning a'zolar bo'yicha ishga tushirgichini saqlab qolish uchun extension moslashtirilganni belgilang Sinflar Belgilangan ishga tushirgich Sinf uchun asosiy ishga tushirgich . U ikkita maqsadga xizmat qiladi: belgilangan ishga tushirgichdir Barcha maydonlar to'ldirilganligini ta'minlaydi Agar sinf meros bo'lib qolgan bo'lsa, u superklassni ishga tushiruvchini chaqiradi class Animal { var name: String init(name: String) { self.name = name } } class Dog: Animal { var breed: String var name: String init(breed: String, name: String) { self.breed = breed super.init(name: name) } } ga qo'ng'iroq qilishdan . Buning sababi shundaki, superklassni ishga tushiruvchi subklass tomonidan bekor qilingan usullarni chaqirishi mumkin, bu esa to'ldirilmagan pastki sinf xususiyatlariga kirishi mumkin. super.init oldin barcha maydonlar to'ldirilishi kerak class Animal { var age: Int init(age: Int) { self.age = age getInfo() } func getInfo() { print("Age: ", age) } } class Dog: Animal { var breed: String init(breed: String, age: Int) { self.breed = breed // imagine we haven't done this super.init(age: age) } override func getInfo() { print("Age: ", age, ", breed: ", breed) } } Shunday qilib, agar o'rnatmaganimizda, biz ish vaqti xatosiga duch kelgan bo'lardik, chunki initsializer sinfidan bekor qilingan usulini chaqirgan bo'lar edi. Bu usul hali to'ldirilmagan mulkiga kirishga harakat qiladi. self.breed = breed Animal Dog getInfo() breed Tuzilmalardan farqli o'laroq, sinflar yashirin a'zolar inisializerini olmaydilar. Agar ishga tushirilmagan xususiyatlar mavjud bo'lsa, kompilyatsiya xatosi yuzaga keladi: class Animal { // ❌ Class 'Animal' has no initializers var age: Int } class Animal { // ✅ var age: Int = 0 } class Animal { // ✅ var age: Int? } class Animal { } // ✅ Qulaylik ishga tushirgich Sinflar, shuningdek, ega bo'lishi mumkin. Belgilangan initsializatorlardan farqli o'laroq, ular noldan ob'ekt yaratmaydi, balki boshqa initsializatorlarning mantig'ini qayta ishlatish orqali ishga tushirish jarayonini soddalashtiradi. qulaylik yaratuvchisiga class Rectangle { var width: Double var height: Double init(width: Double, height: Double) { self.width = width self.height = height } convenience init(side: Double) { self.init(width: side, height: side) // uses a designated initializer of self } } Qulaylik initsializatorlari tayinlangan ishga tushirgichlarni yoki boshqa qulaylik ishga tushirgichlarini chaqirishi mumkin. Oxir-oqibat, belgilangan ishga tushiruvchi har doim chaqiriladi. Qulaylik initsializatorlari har doim gorizontal (self.init) va belgilangan ishga tushirgichlar vertikal (super.init) bo'ladi. Superklassning qulaylik initializerini saqlab qolish Subklass yangi xususiyatlarni e'lon qilishi bilanoq, u supersinfning barcha qulaylik ishga tushirgichlariga kirish huquqini yo'qotadi. class Animal { var age: Int var name: String init(age: Int, name: String) { self.age = age self.name = name } convenience init(age: Int) { self.init(age: age, name: "Default") } convenience init(name: String) { self.init(age: 0, name: name) } } class Dog: Animal { var breed: String init(age: Int, name: String, breed: String) { self.breed = breed super.init(age: age, name: name) } } let dog = Dog(age: 3) // ❌ Missing arguments for parameters 'breed', 'name' in call Bu bekor qilish orqali tuzatilishi mumkin. supersinfning barcha belgilangan initsializatorlarini class Dog: Animal { // ... override init(age: Int, name: String) { self.breed = "Mixed" super.init(age: age, name: name) } } let dog = Dog(age: 3) // ✅ Ko'rinib turibdiki, shu tarzda keyingi kichik sinfda qulaylik ishga tushirgichdan foydalanish uchun initsializatorni bekor qilishingiz kerak. ikkita class GuideDog: Dog { var isTrained: Bool override init(age: Int, name: String) { self.isTrained = false super.init(age: age, name: name) } override init(age: Int, name: String, breed: String) { self.isTrained = false super.init(age: age, name: name, breed: breed) } init(age: Int, name: String, breed: String, isTrained: Bool) { self.isTrained = isTrained super.init(age: age, name: name, breed: breed) } } let dog = GuideDog(age: 3) // ✅ Bekor qilish sonini minimallashtirish Biroq, buning oldini olish uchun foydalanish mumkin. qulaylikni bekor qilish ishga tushirgichidan class Dog: Animal { var breed: String convenience override init(age: Int, name: String) { self.init(age: age, name: name, breed: "Mixed") // self, not super } init(age: Int, name: String, breed: String) { self.breed = breed super.init(age: age, name: name) } } class GuideDog: Dog { var isTrained: Bool // override init(age: Int, name: String) { // self.isTrained = false // // super.init(age: age, name: name, breed: "Mixed") // } convenience override init(age: Int, name: String, breed: String) { self.init(age: age, name: name, breed: breed, isTrained: false) // self, not super } init(age: Int, name: String, breed: String, isTrained: Bool) { self.isTrained = isTrained super.init(age: age, name: name, breed: breed) } } let dog = GuideDog(age: 3) // ✅ Endi bizda har bir kichik sinfda atigi 2 ta aniq ko'rsatilgan boshlang'ich mavjud. o'rniga tayinlangan init deb atashadi. E'tibor bering, qulaylikni bekor qilish initsializatorlari super.init self Bu hiyla Tjeerd in 't Veen tomonidan kitobining 5-bobida batafsil tushuntirilgan, men buni juda tavsiya qilaman. "Swift in Depth" Oraliq xulosa barcha xususiyatlarning to'ldirilishini ta'minlaydi va chaqiradi. Belgilangan ishga tushirgich super.init() belgilangan ishga tushirgichni chaqirish orqali ishga tushirishni osonlashtiradi. Qulaylik ishga tushirgichi agar ular yangi xususiyatlarni e'lon qilsalar, pastki sinflar uchun mavjud bo'lmaydi. Qulaylik ishga tushiruvchisi, Supersinfning tiklash uchun uning barcha belgilangan initsializatorlari bekor qilinishi kerak. qulaylik ishga tushirgichini Qayta belgilashlar sonini minimallashtirish uchun foydalanish mumkin. qulaylikni bekor qilish ishga tushirgichidan Kompilyator yordami Agar quyi sinf yangi parametrlarni kiritmasa, u avtomatik ravishda barcha supersinf initsializatorlarini meros qilib olishini allaqachon muhokama qilgan edik. class Base { let value: Int init() { value = 0 } init(value: Int) { self.value = value } } class Subclass: Base { } let subclass = Subclass() // ✅ let subclass = Subclass(value: 3) // ✅ Biroq, yana bir muhim jihat bor: agar supersinfda faqat bitta tayinlangan initsializer bo'lsa va u parametrsiz bo'lsa ( argumentlarsiz), unda subklassdagi aniq e'lon qilingan initsializatorlar chaqirishga . Bunday holda, Swift kompilyatori qo'ng'iroqni avtomatik ravishda mavjud ga argumentlarsiz . init() super.init() hojat yo'q super.init() kiritadi class Base { init() { } } class Subclass: Base { let secondValue: Int init(secondValue: Int) { self.secondValue = secondValue // ✅ without explicit super.init() } } Kod kompilyatsiya qilinadi, chunki bilvosita chaqiriladi. Bu quyidagi misollarning ba'zilari uchun juda muhimdir. super.init() Majburiy initsializer quyi sinfda asosiy sinf bilan bir xil boshlang'ichga ega bo'lishi kerak bo'lgan barcha holatlarda qo'llaniladi. Bundan tashqari, ni chaqirishi kerak. Quyida ishga tushirgich zarur bo'lgan misollar keltirilgan. required super.init() required Umumiy Umumiy turdagi chaqirish faqat uni deb e'lon qilish orqali mumkin. init required init class Base { } class Subclass: Base { } struct Factory<T: Base> { func initInstance() -> T { // ❌ Constructing an object of class T() // type 'T' with a metatype value } // must use a 'required' initializer } Ushbu kod kompilyatsiya qilinmaydi, chunki pastki sinflari haqida hech narsa bilmaydi. Garchi bu alohida holatda, parametrlarsiz mavjud bo'lsa-da, u yangi maydonni kiritganligini tasavvur qiling: Factory Base Subclass init() class Subclass: Base { let value: Int init(value: Int) { self.value = value } } Bu erda endi bo'sh yo'q, shuning uchun uni e'lon qilish kerak. init required class Base { required init() { } } class Subclass: Base { } struct Factory<T: Base> { static func initInstance() -> T { // ✅ T() } } let subclass = Factory<Subclass>.initInstance() E'tibor bering, biz aniq e'lon qilmagan bo'lsak ham, kompilyator uni biz uchun yaratdi. Bu da muhokama qilindi. avtomatik ravishda meros qilib olinadi va deb nomlanadi. Subclass required init Compiler Assistance required init super.init() class Subclass: Base { required init() { super.init() } } Protokollar Protokollarda e'lon qilingan barcha ishga tushirgichlar qilinishi kerak: required protocol Initable { init() } class InitableObject: Initable { init() { // ❌ Initializer requirement 'init()' can only // be satisfied by a 'required' initializer } // in non-final class 'InitableObject' } Shunga qaramay, bu kompilyator pastki sinf protokolni ishga tushirishni amalga oshirishini ta'minlashi uchun kerak. Biz allaqachon bilganimizdek, bu har doim ham sodir bo'lmaydi - agar bo'lmasa, pastki sinf uni bekor qilishga majbur emas va o'zining boshlang'ichini belgilashi mumkin. init required class IntValue: InitableObject { let value: Int init(value: Int) { self.value = value } } let InitableType: Initable.Type = IntValue.self let initable: Initable = InitableType.init() Albatta, quyidagi kod kompilyatsiya qilinmaydi, chunki emas. Base.init() required class InitableObject: Initable { required init() { } // ✅ } class IntValue: InitableObject { let value: Int required init() { self.value = 0 } init(value: Int) { self.value = value } } Self() Xuddi shunday holat initsializatorini statik usullarda chaqirganda ham yuzaga keladi. Self() class Base { let value: Int init(value: Int) { self.value = value } static func instantiate() -> Self { Self(value: 3) // ❌ Constructing an object of class type 'Self' } // with a metatype value must use a 'required' initializer } Har doimgidek, masala merosga bog'liq: class Subclass: BaseClass { let anotherValue: Int init(anotherValue: Int) { self.anotherValue = anotherValue } } let subclass = Subclass.instantiate() // ❌ class BaseClass { let value: Int required init(value: Int) { // ✅ self.value = value } static func instantiate() -> Self { Self(value: 3) } } bo'lgandan qutulish: required final maqsadi pastki sinflarda initsializatorni amalga oshirishni ta'minlash bo'lgani uchun, tabiiyki, kalit so'zidan foydalanib merosni taqiqlash, boshlang'ichni holda belgilash zaruratini olib tashlaydi. required final required protocol Initable { init() } final class InitableObject: Initable { } // ✅ protocol ValueInitable { init(value: Int) } final class ValueInitableObject: ValueInitable { init(value: Int) { } // ✅ } Oraliq xulosa Agar pastki sinf yangi parametrlarni kiritmasa, u o'zining yuqori sinfidagi barcha boshlang'ichlarni avtomatik ravishda meros qilib oladi. Agar supersinfda faqat parametrlarsiz bo'lsa, u subsinf initsializatorlarida avtomatik ravishda chaqiriladi. init() Jeneriklar, protokollar va da foydalanish uchun pastki sinflarda mavjudligini kafolatlash uchun ishga tushirgich kerak. Self() required UIView() Hujjatlarda topib bo'lmaydigan, lekin hamma joyda sirli ravishda qo'llaniladigan parametrlarsiz ishga tushirgichi haqida qisqacha eslatma. UIView() Sababi, parametrsiz ega bo'lgan dan meros bo'lib o'tadi. , bu ishga tushirgich interfeysida aniq e'lon qilinmagan, ammo u hali ham mavjud: UIView init() NSObject Shuning uchun UIView @available(iOS 2.0, *) @MainActor open class UIView : UIResponder, NSCoding, UIAppearance, UIAppearanceContainer, UIDynamicItem, UITraitEnvironment, UICoordinateSpace, UIFocusItem, UIFocusItemContainer, CALayerDelegate { open class var layerClass: AnyClass { get } public init(frame: CGRect) public init?(coder: NSCoder) open var isUserInteractionEnabled: Bool // no init() Biroq, bu ishga tushirgich kodda ishga tushirilganda yoki Interface Builder orqali ishga tushirilganda chaqiradi. Buning sababi, o'zining ilovasini ta'minlaydi, buni va uchun turli manzillarni qaytarishi bilan tasdiqlanishi mumkin. init(frame:) init(coder:) UIView NSObject.init() method_getImplementation NSObject.init() UIView.init() Muhtaram eslatmalar Muvaffaqiyatsiz ishga tushirish Muvaffaqiyatsiz init - bu ixtiyoriyni qaytaradigan final class Failable { let positiveValue: Int init?(value: Int) { guard value > 0 else { return nil } positiveValue = value } } Enum Xom qiymatga ega bo'lgan raqamlar bepul init?(rawValue:) enum Direction: String { case north case west case south case east } let north = Direction(rawValue: "north") Enumlar uchun maxsus init ham yaratishingiz mumkin. Barcha enum initslari belgilashi kerak. self enum DeviceType { case phone case tablet init(screenWidth: Int) { self = screenWidth > 800 ? .tablet : .phone } } Yakuniy xulosa Biz Swift-da initsializatorlarning barcha muhim jihatlarini ko'rib chiqdik: Initsializatorda barcha maydonlar to'ldirilishi kerak. Ixtiyoriy xususiyatlari sukut bo'yicha teng. var nil Strukturalar bepul oladi. a'zo bo'yicha ishga tushirgich Maxsus ishga tushirgich aniqlanganda . a'zolar bo'yicha ishga tushirgich yo'qoladi barcha maydonlarni to'ldirishni ta'minlaydi va ni chaqiradi. Belgilangan ishga tushirgich super.init() belgilangan ishga tushirgichni chaqirish orqali ishga tushirishni osonlashtiradi. Qulaylik ishga tushirgichi Qulaylik initsializatorlari har doim ( ) va belgilangan ishga tushirgichlar ( ) bo'ladi. gorizontal self.init vertikal super.init agar ular yangi xususiyatlarni e'lon qilsalar, pastki sinflar uchun mavjud bo'lmaydi. Qulaylik ishga tushiruvchisi, Supersinfning tiklash uchun uning barcha belgilangan initsializatorlari bekor qilinishi kerak. qulaylik ishga tushirgichini Qayta belgilashlar sonini kamaytirish uchun foydalanish mumkin. qulaylikni bekor qilish boshlang'ichidan Agar kichik sinf yangi parametrlarni kiritmasa, u o'zining yuqori sinfidagi barcha boshlang'ichlarni avtomatik ravishda meros qilib oladi. Agar supersinfda faqat parametrlarsiz bo'lsa, u avtomatik ravishda subsinf initsializatorlarida chaqiriladi. init() uning generiklar, protokollar va da foydalanish uchun quyi sinflarda mavjudligini ta'minlaydi. Kerakli initsializator Self() yoki chaqiradi. UIView.init() UIView.init(frame:) UIView.init(coder:) ixtiyoriyni qaytaradi. Muvaffaqiyatsiz ishga tushirgich Xom qiymatga ega bo'lgan enumlar bepul . init?(rawValue:) Barcha enum boshlang'ichlari belgilashi kerak. self Umid qilamanki, siz ushbu maqolada foydali narsalarni topdingiz. Agar biror narsa noaniq bo'lib qolsa yoki siz noaniqlik topsangiz, Telegram orqali bepul tushuntirish uchun men bilan bog'laning: . @kfamyn Tegishli havolalar Swift dasturlash tili (6) / Initializatsiya Swift in Depth, Tjeerd tomonidan 't Veen Telegram - @kfamyn