У мене виникають проблеми, щоб зрозуміти різницю між ними або мету convenience init
.
У мене виникають проблеми, щоб зрозуміти різницю між ними або мету convenience init
.
Відповіді:
Стандарт init
:
Призначені ініціалізатори є основними ініціалізаторами для класу. Призначений ініціалізатор повністю ініціалізує всі властивості, введені цим класом, і викликає відповідний ініціалізатор суперкласу, щоб продовжити процес ініціалізації вгору по ланцюжку суперкласу.
convenience init
:
Зручні ініціалізатори є вторинними, підтримуючи ініціалізатори для класу. Ви можете визначити зручний ініціалізатор для виклику призначеного ініціалізатора з того ж класу, що і ініціалізатор зручності, з деякими параметрами призначеного ініціалізатора, встановленими на значення за замовчуванням. Ви також можете визначити зручний ініціалізатор для створення екземпляра цього класу для конкретного випадку використання або типу вхідного значення.
відповідно до документації Swift
У двох словах, це означає, що ви можете використовувати зручний ініціалізатор, щоб зробити виклик призначеного ініціалізатора швидшим та "зручнішим". Отже, зручні ініціалізатори вимагають використання self.init
замість того, що super.init
ви могли б побачити в перевизначенні призначеного ініціалізатора.
Приклад псевдокоду:
init(param1, param2, param3, ... , paramN) {
// code
}
// can call this initializer and only enter one parameter,
// set the rest as defaults
convenience init(myParamN) {
self.init(defaultParam1, defaultParam2, defaultParam3, ... , myParamN)
}
Я багато використовую їх при створенні власних подань та таких, які мають довгі ініціалізатори з головним чином за замовчуванням. Документи краще пояснюють роботу, ніж я можу, перевірте їх!
Зручні ініціалізатори використовуються, коли у вас є якийсь клас з великою кількістю властивостей, що робить своєрідним "болісним" завжди ініціалізувати дотепність із усіма цими змінними, тому що ви робите із зручним ініціалізатором, це те, що ви просто передаєте деякі змінні для ініціалізації об'єкт, а решті призначте значення за замовчуванням. На веб-сайті Рея Вендерліха є дуже хороше відео, не впевнене, що воно безкоштовне чи ні, тому що у мене платний рахунок. Ось приклад, де ви можете бачити, що замість того, щоб ініціалізувати мій об’єкт усіма цими змінними, я просто даю йому заголовок.
struct Scene {
var minutes = 0
}
class Movie {
var title: String
var author: String
var date: Int
var scenes: [Scene]
init(title: String, author: String, date: Int) {
self.title = title
self.author = author
self.date = date
scenes = [Scene]()
}
convenience init(title:String) {
self.init(title:title, author: "Unknown", date:2016)
}
func addPage(page: Scene) {
scenes.append(page)
}
}
var myMovie = Movie(title: "my title") // Using convenicence initializer
var otherMovie = Movie(title: "My Title", author: "My Author", date: 12) // Using a long normal initializer
Ось простий приклад, взятий із порталу розробників Apple .
В основному призначеним ініціалізатором є init(name: String)
, він гарантує, що всі збережені властивості ініціалізуються.
init()
Зручність ініціалізатор, не беручи ніяких аргументів, автоматично встановлює значення name
зберігається майна [Unnamed]
, використовуючи призначений ініціалізатор.
class Food {
let name: String
// MARK: - designated initializer
init(name: String) {
self.name = name
}
// MARK: - convenience initializer
convenience init() {
self.init(name: "[Unnamed]")
}
}
// MARK: - Examples
let food = Food(name: "Cheese") // name will be "Cheese"
let food = Food() // name will be "[Unnamed]"
Це корисно, коли ви маєте справу з великими класами, принаймні з кількома збереженими властивостями. Я б порекомендував прочитати трохи більше про додаткові засоби та спадщину на порталі Apple Developer.
Для мене convenience initializers
корисні, якщо потрібно зробити щось більше, ніж просто встановити значення за замовчуванням для властивості класу.
В іншому випадку я б просто встановив значення за замовчуванням у init
визначенні, наприклад:
class Animal {
var race: String // enum might be better but I am using string for simplicity
var name: String
var legCount: Int
init(race: String = "Dog", name: String, legCount: Int = 4) {
self.race = race
self.name = name
self.legCount = legCount // will be 4 by default
}
}
Однак, можливо, слід зробити щось більше, ніж просто встановити значення за замовчуванням, і ось тут стане convenience initializers
в нагоді:
extension Animal {
convenience init(race: String, name: String) {
var legs: Int
if race == "Dog" {
legs = 4
} else if race == "Spider" {
legs = 8
} else {
fatalError("Race \(race) needs to be implemented!!")
}
// will initialize legCount automatically with correct number of legs if race is implemented
self.init(race: race, name: name, legCount: legs)
}
}
// default init with all default values used
let myFirstDog = Animal(name: "Bello")
// convenience init for Spider as race
let mySpider = Animal(race: "Spider", name: "Itzy")
// default init with all parameters set by user
let myOctopus = Animal(race: "Octopus", name: "Octocat", legCount: 16)
// convenience init with Fatal error: Race AlienSpecies needs to be implemented!!
let myFault = Animal(race: "AlienSpecies", name: "HelloEarth")
Зручний ініціалізатор можна визначити у розширенні класу . Але стандартний - не може.
Це має сенс, якщо у вашому випадку використання є виклик ініціалізатора в іншому ініціалізаторі того ж класу .
Спробуйте зробити це на дитячому майданчику
class Player {
let name: String
let level: Int
init(name: String, level: Int) {
self.name = name
self.level = level
}
init(name: String) {
self.init(name: name, level: 0) //<- Call the initializer above?
//Sorry you can't do that. How about adding a convenience keyword?
}
}
Player(name:"LoseALot")
З зручністю ключове слово
class Player {
let name: String
let level: Int
init(name: String, level: Int) {
self.name = name
self.level = level
}
//Add the convenience keyword
convenience init(name: String) {
self.init(name: name, level: 0) //Yes! I am now allowed to call my fellow initializer!
}
}
Примітка: Прочитайте весь текст
Призначені ініціалізатори є основними ініціалізаторами для класу. Призначений ініціалізатор повністю ініціалізує всі властивості, введені цим класом, і викликає відповідний ініціалізатор суперкласу, щоб продовжити процес ініціалізації до ланцюжка суперкласу.
Зручні ініціалізатори є вторинними, підтримуючи ініціалізатори для класу. Ви можете визначити зручний ініціалізатор для виклику призначеного ініціалізатора з того ж класу, що і ініціалізатор зручності, з деякими параметрами призначеного ініціалізатора, встановленими за замовчуванням.
Призначені ініціалізатори для класів записуються так само, як і прості ініціалізатори для типів значень:
init(parameters) {
statements
}
Ініціалізатори зручності написані в тому ж стилі, але з модифікатором зручності, розміщеним перед ключовим словом init, відокремленим пробілом:
convenience init(parameters) {
statements
}
Практичним прикладом є такі:
class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
}
}
let namedMeat = Food(name: "Bacon")
// namedMeat's name is "Bacon”
Ініціатор init (name: String) із класу Food надається як призначений ініціалізатор, оскільки забезпечує повну ініціалізацію всіх збережених властивостей нового екземпляра Food. Клас Food не має суперкласу, тому ініціалізатору init (name: String) не потрібно викликати super.init (), щоб завершити його ініціалізацію.
“Клас Food також надає зручний ініціалізатор, init (), без аргументів. Ініціатор init () надає ім’я заповнювача за замовчуванням для нового продукту, делегуючи його до init класу Food (name: String) зі значенням імені [Без імені]:
“let mysteryMeat = Food()
// mysteryMeat's name is "[Unnamed]”
Другим класом в ієрархії є підклас їжі, який називається RecipeIngredient. Клас RecipeIngredient моделює інгредієнт рецепту приготування. Він вводить властивість Int, що називається кількість (на додаток до властивості імені, яке вона успадковує від Food), і визначає два ініціалізатори для створення екземплярів RecipeIngredient:
class RecipeIngredient: Food {
var quantity: Int
init(name: String, quantity: Int) {
self.quantity = quantity
super.init(name: name)
}
override convenience init(name: String) {
self.init(name: name, quantity: 1)
}
}
Клас RecipeIngredient має єдиний призначений ініціалізатор, init (ім'я: String, кількість: Int), який можна використовувати для заповнення всіх властивостей нового екземпляра RecipeIngredient. Цей ініціалізатор починається з присвоєння аргументу переданої кількості властивості кількості, яка є єдиною новою властивістю, введеною RecipeIngredient. Після цього ініціалізатор делегує до ініціатора init (name: String) класу Food.
сторінка: 536 Витяг з: Apple Inc. “Мова програмування Swift (Swift 4).” iBooks. https://itunes.apple.com/pk/book/the-swift-programming-language-swift-4-0-3/id881256329?mt=11
Тож це дуже зручно, коли вам не потрібно вказувати кожну властивість для класу. Так, наприклад, якщо я хочу створити всі пригоди зі стартовим значенням HP 100, я б використав цей наступний зручний init і просто додав ім'я. Це збирається значно скоротити код.
class Adventure {
// Instance Properties
var name: String
var hp: Int
let maxHealth: Int = 100
// Optionals
var specialMove: String?
init(name: String, hp: Int) {
self.name = name
self.hp = hp
}
convenience init(name: String){
self.init(name: name, hp: 100)
}
}
Усі відповіді звучать добре, але давайте зрозуміємо це на простому прикладі
class X{
var temp1
init(a: Int){
self.temp1 = a
}
Тепер ми знаємо, що клас може успадкувати інший клас, Отже
class Z: X{
var temp2
init(a: Int, b: Int){
self.temp2 = b
super.init(a: a)
}
Тепер, у цьому випадку, під час створення екземпляра для класу Z, вам доведеться вказати обидва значення "a" і "b".
let z = Z(a: 1, b: 2)
Але що, якщо ви хочете лише передати значення b і хочете, щоб відпочинок прийняв значення за замовчуванням для інших, тоді в цьому випадку вам потрібно ініціалізувати інші значення за замовчуванням. Але почекайте як ?, для цього U потрібно встановити це задовго до того, як у класі.
//This is inside the class Z, so consider it inside class Z's declaration
convenience init(b: Int){
self.init(a: 0, b: b)
}
convenience init(){
self.init(a: 0, b: 0)
}
І тепер ви можете створювати екземпляри класу Z, надаючи для змінних деякі, усі або взагалі відсутні значення.
let z1 = Z(b: 2)
let z2 = Z()