Tengo problemas para entender la diferencia entre ambos o el propósito del convenience init
.
swift
initialization
convenience-methods
Chino Pan
fuente
fuente
Respuestas:
Estándar
init
:convenience init
: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 delsuper.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!
fuente
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
fuente
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 laname
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.
fuente
Donde los inicializadores de conveniencia superan la configuración de los valores predeterminados de los parámetros
Para mí,
convenience initializers
son útiles si hay más que hacer que simplemente establecer un valor predeterminado para una propiedad de clase.Implementación de clase con init () designado
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 } }
Extensión de clase con comodidad init ()
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) } }
Ejemplos de uso
// 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")
fuente
Se puede definir un inicializador de conveniencia en una extensión de clase . Pero uno estándar, no puede.
fuente
la
convenience init
hace que sea opcional para inicializar una clase con valores.fuente
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! } }
fuente
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
fuente
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) } }
fuente
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()
fuente