Tengo problemas para entender la diferencia entre ambos o el propósito del convenience init
.
Tengo problemas para entender la diferencia entre ambos o el propósito del convenience init
.
Respuestas:
Estándar init
:
Los inicializadores designados son los inicializadores principales de una clase. Un inicializador designado inicializa completamente todas las propiedades introducidas por esa clase y llama a un inicializador de superclase apropiado para continuar el proceso de inicialización en la cadena de superclase.
convenience init
:
Los inicializadores de conveniencia son secundarios y admiten inicializadores para una clase. Puede definir un inicializador de conveniencia para llamar a un inicializador designado de la misma clase que el inicializador de conveniencia con algunos de los parámetros del inicializador designado establecidos en valores predeterminados. También puede definir un inicializador de conveniencia para crear una instancia de esa clase para un caso de uso específico o un tipo de valor de entrada.
según la documentación de Swift
En pocas palabras, esto significa que puede usar un inicializador conveniente para hacer que llamar a un inicializador designado sea más rápido y más "conveniente". Por lo tanto, los inicializadores de conveniencia requieren el uso de en self.init
lugar del super.init
que podría ver en una anulación de un inicializador designado.
ejemplo de pseudocódigo:
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)
}
Los uso mucho al crear vistas personalizadas y que tienen inicializadores largos con principalmente valores predeterminados. Los documentos hacen un mejor trabajo explicando que yo, ¡échales un vistazo!
Los inicializadores de conveniencia se usan cuando tienes una clase con muchas propiedades que hacen que sea un poco "doloroso" inicializar siempre el ingenio con todas esas variables, así que lo que haces con el inicializador de conveniencia es que simplemente pasas algunas de las variables para inicializar el objeto y asigne el resto con un valor predeterminado. Hay un video muy bueno en el sitio web de Ray Wenderlich, no estoy seguro de que sea gratis o no porque tengo una cuenta paga. Aquí hay un ejemplo donde puede ver que en lugar de inicializar mi objeto con todas esas variables, solo le estoy dando un título.
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
Aquí hay un ejemplo simple, tomado del portal de desarrolladores de Apple .
Básicamente, el inicializador designado es el init(name: String)
, asegura que todas las propiedades almacenadas se inicialicen.
El init()
inicializador de conveniencia, que no acepta argumentos, establece automáticamente el valor de la name
propiedad almacenada en [Unnamed]
mediante el inicializador designado.
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]"
Es útil, cuando se trata de clases grandes, con al menos algunas propiedades almacenadas. Recomendaría leer un poco más sobre opcionales y herencia en el portal de desarrolladores de Apple.
Para mí, convenience initializers
son útiles si hay más que hacer que simplemente establecer un valor predeterminado para una propiedad de clase.
De lo contrario, simplemente establecería el valor predeterminado en la init
definición, por ejemplo:
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
}
}
Sin embargo, puede haber más cosas que hacer que simplemente establecer un valor predeterminado, y ahí es donde resulta convenience initializers
útil:
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")
Se puede definir un inicializador de conveniencia en una extensión de clase . Pero uno estándar, no puede.
Tiene sentido si su caso de uso es llamar a un inicializador en otro inicializador en la misma clase .
Intenta hacer esto en el patio de recreo
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")
Con palabra clave de conveniencia
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!
}
}
Nota: lea todo el texto
Los inicializadores designados son los inicializadores principales de una clase. Un inicializador designado inicializa completamente todas las propiedades introducidas por esa clase y llama a un inicializador de superclase apropiado para continuar el proceso de inicialización hasta la cadena de superclase.
Los inicializadores de conveniencia son secundarios y admiten inicializadores para una clase. Puede definir un inicializador de conveniencia para llamar a un inicializador designado de la misma clase que el inicializador de conveniencia con algunos de los parámetros del inicializador designado configurados como predeterminados.
Los inicializadores designados para clases se escriben de la misma manera que los inicializadores simples para tipos de valor:
init(parameters) {
statements
}
Los inicializadores de conveniencia están escritos en el mismo estilo, pero con el modificador de conveniencia colocado antes de la palabra clave init, separados por un espacio:
convenience init(parameters) {
statements
}
Un ejemplo práctico es el siguiente:
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”
El inicializador init (name: String) de la clase Food se proporciona como un inicializador designado porque garantiza que todas las propiedades almacenadas de una nueva instancia de Food se inicialicen por completo. La clase Food no tiene una superclase, por lo que el inicializador init (nombre: String) no necesita llamar a super.init () para completar su inicialización.
“La clase Food también proporciona un inicializador de conveniencia, init (), sin argumentos. El inicializador init () proporciona un nombre de marcador de posición predeterminado para un nuevo alimento al delegar en el init de la clase de Alimentos (nombre: Cadena) con un valor de nombre de [Sin nombre]: "
“let mysteryMeat = Food()
// mysteryMeat's name is "[Unnamed]”
La segunda clase en la jerarquía es una subclase de alimentos llamada RecipeIngredient. La clase RecipeIngredient modela un ingrediente en una receta de cocina. Introduce una propiedad Int llamada cantidad (además de la propiedad de nombre que hereda de Food) y define dos inicializadores para crear instancias de 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)
}
}
La clase RecipeIngredient tiene un único inicializador designado, init (nombre: String, cantidad: Int), que se puede usar para completar todas las propiedades de una nueva instancia de RecipeIngredient. Este inicializador comienza asignando el argumento de cantidad pasado a la propiedad de cantidad, que es la única propiedad nueva introducida por RecipeIngredient. Después de hacerlo, el inicializador delega hasta el inicializador init (nombre: String) de la clase Food.
página: 536 Extracto de: Apple Inc. "El lenguaje de programación Swift (Swift 4)". iBooks. https://itunes.apple.com/pk/book/the-swift-programming-language-swift-4-0-3/id881256329?mt=11
Por tanto, resulta útil cuando no es necesario especificar todas y cada una de las propiedades de una clase. Entonces, por ejemplo, si quiero crear todas las aventuras con un valor de HP inicial de 100, usaría el siguiente inicio de conveniencia y solo agregaría un nombre. Esto va a reducir mucho el código.
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)
}
}
Todas las respuestas suenan bien pero, entendamos con un ejemplo simple
class X{
var temp1
init(a: Int){
self.temp1 = a
}
Ahora, sabemos que una clase puede heredar otra clase, así que
class Z: X{
var temp2
init(a: Int, b: Int){
self.temp2 = b
super.init(a: a)
}
Ahora, en este caso, mientras crea una instancia para la clase Z, deberá proporcionar los valores 'a' y 'b'.
let z = Z(a: 1, b: 2)
Pero, ¿qué pasa si solo desea pasar el valor de b y desea que el resto tome el valor predeterminado para otros? Entonces, en ese caso, debe inicializar otros valores con un valor predeterminado. Pero espera, ¿cómo ?, para eso U necesitas configurarlo bien antes solo en la clase.
//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)
}
Y ahora, puede crear instancias de clase Z proporcionando algunos, todos o ninguno valores para las variables.
let z1 = Z(b: 2)
let z2 = Z()