Мне трудно понять разницу между обоими или цель convenience init
.
swift
initialization
convenience-methods
Чино Пан
источник
источник
Ответы:
Стандарт
init
:convenience init
:согласно Swift Documentation
Вкратце, это означает, что вы можете использовать удобный инициализатор, чтобы сделать вызов назначенного инициализатора более быстрым и «удобным». Таким образом, для удобных инициализаторов требуется использование
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 ()
В противном случае я бы просто установил значение по умолчанию в
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 } }
Расширение класса с удобством init ()
Однако здесь может быть что-то большее, чем просто установить значение по умолчанию, и это то, где
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")
источник
Удобный инициализатор можно определить в расширении класса . А вот стандартный - не может.
источник
convenience init
делает необязательным для инициализации класса со значениями.источник
Это имеет смысл, если ваш вариант использования - вызвать инициализатор в другом инициализаторе в том же классе .
Попробуйте сделать это на детской площадке
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 () предоставляет имя заполнителя по умолчанию для нового блюда, делегируя его классу Food init (name: String) со значением имени [Безымянный]: »
“let mysteryMeat = Food() // mysteryMeat's name is "[Unnamed]”
Второй класс в иерархии - это подкласс Food под названием RecipeIngredient. Класс RecipeIngredient моделирует ингредиент в рецепте приготовления. Он вводит свойство Int, называемое количеством (в дополнение к свойству name, которое оно наследует от 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 и хотите, чтобы rest принимал значение по умолчанию для других, тогда в этом случае вам нужно инициализировать другие значения значением по умолчанию. Но подождите, как ?, для этого 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()
источник