Swift Inicialización
La inicialización es el proceso de preparación de una instancia de una clase, estructura o enumeración para su uso. Este proceso incluye establecer valores iniciales para cada propiedad almacenada de la instancia y realizar las tareas de preparación e inicialización necesarias.
El inicializador de Swift usa el método init().
A diferencia de los inicializadores en Objective-C, los inicializadores de Swift no necesitan devolver un valor; su tarea principal es garantizar que una nueva instancia esté correctamente inicializada antes de su primer uso.
Las instancias de clase también pueden definir un desinicializador (deinitializer) para realizar tareas de limpieza de memoria antes de que la instancia de clase sea liberada.
Asignación inicial de propiedades almacenadas
Las clases y estructuras deben establecer un valor inicial apropiado para todas sus propiedades almacenadas cuando se crea una instancia.
Cuando a las propiedades almacenadas se les asigna un valor en un inicializador, el valor se establece directamente y no se activa ningún observador de propiedades.
Proceso de asignación de propiedades almacenadas en un inicializador:
Crear el valor inicial.
Especificar valores de propiedad predeterminados en la definición de la propiedad.
Inicializar la instancia y llamar al método init().
Inicializador
El inicializador se llama al crear una nueva instancia de un tipo particular. Su forma más simple se asemeja a un método de instancia sin parámetros, nombrado con la palabra clave init.
Sintaxis
init()
{
// 实例化后执行的代码
}
Ejemplo
La siguiente estructura define un inicializador init sin parámetros, y en él inicializa los valores de las propiedades almacenadas length y breadth a 6 y 12:
struct rectangle {
var length: Double
var breadth: Double
init() {
length = 6
breadth = 12
}
}
var area = rectangle()
print("矩形面积为 \(area.length*area.breadth)")
La salida de ejecución del programa anterior es:
矩形面积为 72.0
Valores de propiedad predeterminados
Podemos establecer valores iniciales para las propiedades almacenadas en el inicializador; asimismo, también podemos establecer valores predeterminados para ellas al declarar la propiedad.
Usar valores predeterminados hace que tu inicializador sea más conciso y claro, y permite inferir automáticamente el tipo de la propiedad a partir del valor predeterminado.
En el siguiente ejemplo, establecemos valores predeterminados al declarar las propiedades:
struct rectangle {
// 设置默认值
var length = 6
var breadth = 12
}
var area = rectangle()
print("矩形的面积为 \(area.length*area.breadth)")
La salida de ejecución del programa anterior es:
矩形面积为 72
Parámetros del inicializador
Puedes proporcionar parámetros de inicialización al definir el inicializador init(), como se muestra a continuación:
struct Rectangle {
var length: Double
var breadth: Double
var area: Double
init(fromLength length: Double, fromBreadth breadth: Double) {
self.length = length
self.breadth = breadth
area = length * breadth
}
init(fromLeng leng: Double, fromBread bread: Double) {
self.length = leng
self.breadth = bread
area = leng * bread
}
}
let ar = Rectangle(fromLength: 6, fromBreadth: 12)
print("面积为: \(ar.area)")
let are = Rectangle(fromLeng: 36, fromBread: 12)
print("面积为: \(are.area)")
La salida de ejecución del programa anterior es:
面积为: 72.0 面积为: 432.0
Nombres de parámetros internos y externos
Al igual que los parámetros de funciones y métodos, los parámetros de inicialización también tienen un nombre de parámetro interno, que se usa dentro del inicializador, y un nombre de parámetro externo, que se usa al llamar al inicializador.
Sin embargo, a diferencia de las funciones y métodos, los inicializadores no tienen un nombre distintivo antes del paréntesis. Por lo tanto, al llamar a un inicializador, éste se determina principalmente por los nombres y tipos de sus parámetros.
Si no proporcionas un nombre externo para un parámetro al definir el inicializador, Swift genera automáticamente un nombre externo igual al nombre interno para cada parámetro del inicializador.
struct Color {
let red, green, blue: Double
init(red: Double, green: Double, blue: Double) {
self.red = red
self.green = green
self.blue = blue
}
init(white: Double) {
red = white
green = white
blue = white
}
}
// 创建一个新的Color实例,通过三种颜色的外部参数名来传值,并调用构造器
let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)
print("red 值为: \(magenta.red)")
print("green 值为: \(magenta.green)")
print("blue 值为: \(magenta.blue)")
// 创建一个新的Color实例,通过三种颜色的外部参数名来传值,并调用构造器
let halfGray = Color(white: 0.5)
print("red 值为: \(halfGray.red)")
print("green 值为: \(halfGray.green)")
print("blue 值为: \(halfGray.blue)")
La salida de ejecución del programa anterior es:
red 值为: 1.0 green 值为: 0.0 blue 值为: 1.0 red 值为: 0.5 green 值为: 0.5 blue 值为: 0.5
Parámetros sin nombre externo
Si no deseas proporcionar un nombre externo para un parámetro del inicializador, puedes usar un guion bajo (`_`)_para describir explícitamente su nombre externo.
struct Rectangle {
var length: Double
init(frombreadth breadth: Double) {
length = breadth * 10
}
init(frombre bre: Double) {
length = bre * 30
}
//不提供外部名字
init(_ area: Double) {
length = area
}
}
// 调用不提供外部名字
let rectarea = Rectangle(180.0)
print("面积为: \(rectarea.length)")
// 调用不提供外部名字
let rearea = Rectangle(370.0)
print("面积为: \(rearea.length)")
// 调用不提供外部名字
let recarea = Rectangle(110.0)
print("面积为: \(recarea.length)")
La salida de ejecución del programa anterior es:
面积为: 180.0 面积为: 370.0 面积为: 110.0
Tipos de propiedad opcionales
Si el tipo que defines contiene una propiedad almacenada que lógicamente puede tener un valor vacío, debes definirla como un tipo opcional (optional type).
Cuando una propiedad almacenada se declara como opcional, se inicializa automáticamente como nil (vacío).
struct Rectangle {
var length: Double?
init(frombreadth breadth: Double) {
length = breadth * 10
}
init(frombre bre: Double) {
length = bre * 30
}
init(_ area: Double) {
length = area
}
}
let rectarea = Rectangle(180.0)
print("面积为:\(rectarea.length)")
let rearea = Rectangle(370.0)
print("面积为:\(rearea.length)")
let recarea = Rectangle(110.0)
print("面积为:\(recarea.length)")
La salida de ejecución del programa anterior es:
面积为:Optional(180.0) 面积为:Optional(370.0) 面积为:Optional(110.0)
Modificar propiedades constantes durante la inicialización
Siempre que el valor de la constante pueda determinarse antes de que finalice la inicialización, puedes modificar el valor de una propiedad constante en cualquier momento durante el proceso de inicialización.
Para una instancia de clase, su propiedad constante solo puede modificarse durante la inicialización de la clase donde está definida; no puede modificarse en una subclase.
Aunque la propiedad length ahora es constante, aún podemos establecer su valor en el inicializador de su clase:
struct Rectangle {
let length: Double?
init(frombreadth breadth: Double) {
length = breadth * 10
}
init(frombre bre: Double) {
length = bre * 30
}
init(_ area: Double) {
length = area
}
}
let rectarea = Rectangle(180.0)
print("面积为:\(rectarea.length)")
let rearea = Rectangle(370.0)
print("面积为:\(rearea.length)")
let recarea = Rectangle(110.0)
print("面积为:\(recarea.length)")
La salida de ejecución del programa anterior es:
面积为:Optional(180.0) 面积为:Optional(370.0) 面积为:Optional(110.0)
Inicializador predeterminado
El inicializador predeterminado crea simplemente una instancia con todos los valores de sus propiedades establecidos a sus valores predeterminados:
En el siguiente ejemplo, todas las propiedades de la clase ShoppingListItem tienen valores predeterminados y, al ser una clase base sin superclase, obtiene automáticamente un inicializador predeterminado que puede establecer valores predeterminados para todas las propiedades.
class ShoppingListItem {
var name: String?
var quantity = 1
var purchased = false
}
var item = ShoppingListItem()
print("名字为: \(item.name)")
print("数理为: \(item.quantity)")
print("是否付款: \(item.purchased)")
La salida de ejecución del programa anterior es:
名字为: nil 数理为: 1 是否付款: false
Inicializador miembro a miembro de estructuras
Si una estructura proporciona valores predeterminados para todas sus propiedades almacenadas y no define ningún inicializador personalizado, puede obtener automáticamente un inicializador miembro a miembro.
Al llamar al inicializador miembro a miembro, se pasan valores mediante nombres de parámetros iguales a los nombres de las propiedades miembro, logrando así la asignación inicial de las propiedades miembro.
En el siguiente ejemplo se define una estructura Rectangle que contiene dos propiedades, length y breadth. Swift puede inferir automáticamente sus tipos como Double a partir de los valores iniciales 100.0 y 200.0.
struct Rectangle {
var length = 100.0, breadth = 200.0
}
let area = Rectangle(length: 24.0, breadth: 32.0)
print("矩形的面积: \(area.length)")
print("矩形的面积: \(area.breadth)")
Debido a que estas dos propiedades almacenadas tienen valores predeterminados, la estructura Rectangle obtiene automáticamente un inicializador miembro a miembro init(width:height:). Puedes usarlo para crear nuevas instancias de Rectangle.
La salida de ejecución del programa anterior es:
矩形的面积: 24.0 矩形的面积: 32.0
Delegación de inicializadores para tipos de valor
Un inicializador puede llamar a otros inicializadores para completar parte del proceso de inicialización de una instancia. Este proceso se denomina delegación de inicializadores y reduce la duplicación de código entre varios inicializadores.
En el siguiente ejemplo, la estructura Rect llama a los procesos de inicialización de Size y Point:
struct Size {
var width = 0.0, height = 0.0
}
struct Point {
var x = 0.0, y = 0.0
}
struct Rect {
var origin = Point()
var size = Size()
init() {}
init(origin: Point, size: Size) {
self.origin = origin
self.size = size
}
init(center: Point, size: Size) {
let originX = center.x - (size.width / 2)
let originY = center.y - (size.height / 2)
self.init(origin: Point(x: originX, y: originY), size: size)
}
}
// origin和size属性都使用定义时的默认值Point(x: 0.0, y: 0.0)和Size(width: 0.0, height: 0.0):
let basicRect = Rect()
print("Size 结构体初始值: \(basicRect.size.width, basicRect.size.height) ")
print("Rect 结构体初始值: \(basicRect.origin.x, basicRect.origin.y) ")
// 将origin和size的参数值赋给对应的存储型属性
let originRect = Rect(origin: Point(x: 2.0, y: 2.0),
size: Size(width: 5.0, height: 5.0))
print("Size 结构体初始值: \(originRect.size.width, originRect.size.height) ")
print("Rect 结构体初始值: \(originRect.origin.x, originRect.origin.y) ")
//先通过center和size的值计算出origin的坐标。
//然后再调用(或代理给)init(origin:size:)构造器来将新的origin和size值赋值到对应的属性中
let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
size: Size(width: 3.0, height: 3.0))
print("Size 结构体初始值: \(centerRect.size.width, centerRect.size.height) ")
print("Rect 结构体初始值: \(centerRect.origin.x, centerRect.origin.y) ")
La salida de ejecución del programa anterior es:
Size 结构体初始值: (0.0, 0.0) Rect 结构体初始值: (0.0, 0.0) Size 结构体初始值: (5.0, 5.0) Rect 结构体初始值: (2.0, 2.0) Size 结构体初始值: (3.0, 3.0) Rect 结构体初始值: (2.5, 2.5)
Reglas de delegación de inicializadores
| Tipos de valor | Tipos de clase |
|---|---|
| No admiten herencia, por lo que el proceso de delegación de inicializadores es relativamente simple, ya que solo pueden delegar en otros inicializadores proporcionados por el propio tipo de valor. Puedes usar `self.init` en un inicializador personalizado para referenciar otros inicializadores del mismo tipo de valor. | Pueden heredar de otras clases, lo que significa que la clase tiene la responsabilidad de garantizar que todas sus propiedades almacenadas heredadas se inicialicen correctamente durante la inicialización. |
Herencia de clases e inicialización
Swift proporciona dos tipos de inicializadores de clase para garantizar que todas las propiedades almacenadas de las instancias de clase obtengan valores iniciales: inicializadores designados e inicializadores de conveniencia.
| Inicializador designado | Inicializador de conveniencia |
| El inicializador principal de la clase. | El inicializador secundario y auxiliar de la clase. |
| Inicializa todas las propiedades introducidas por la clase y recorre la cadena de superclases llamando a los inicializadores de las superclases para lograr su inicialización. | Puedes definir un inicializador de conveniencia para llamar a un inicializador designado de la misma clase y proporcionar valores predeterminados para sus parámetros. También puedes definir un inicializador de conveniencia para crear una instancia con un propósito especial o para una entrada específica. |
| Cada clase debe tener al menos un inicializador designado. | Proporciona inicializadores de conveniencia a una clase solo cuando sea necesario. |
Init(parameters) {
statements
}
|
convenience init(parameters) {
statements
}
|
Ejemplo de inicializador designado
class mainClass {
var no1 : Int // 局部存储变量
init(no1 : Int) {
self.no1 = no1 // 初始化
}
}
class subClass : mainClass {
var no2 : Int // 新的子类存储变量
init(no1 : Int, no2 : Int) {
self.no2 = no2 // 初始化
super.init(no1:no1) // 初始化超类
}
}
let res = mainClass(no1: 10)
let res2 = subClass(no1: 10, no2: 20)
print("res 为: \(res.no1)")
print("res2 为: \(res2.no1)")
print("res2 为: \(res2.no2)")
La salida de ejecución del programa anterior es:
res 为: 10 res 为: 10 res 为: 20
Ejemplo de inicializador de conveniencia
class mainClass {
var no1 : Int // 局部存储变量
init(no1 : Int) {
self.no1 = no1 // 初始化
}
}
class subClass : mainClass {
var no2 : Int
init(no1 : Int, no2 : Int) {
self.no2 = no2
super.init(no1:no1)
}
// 便利方法只需要一个参数
override convenience init(no1: Int) {
self.init(no1:no1, no2:0)
}
}
let res = mainClass(no1: 20)
let res2 = subClass(no1: 30, no2: 50)
print("res 为: \(res.no1)")
print("res2 为: \(res2.no1)")
print("res2 为: \(res2.no2)")
La salida de ejecución del programa anterior es:
res 为: 20 res2 为: 30 res2 为: 50
Herencia y sobrescritura de inicializadores
Las subclases en Swift no heredan los inicializadores de la superclase de forma predeterminada.
Los inicializadores de la superclase solo se heredan en circunstancias determinadas y seguras.
Cuando sobrescribes un inicializador designado de la superclase, necesitas escribir el modificador override.
class SuperClass {
var corners = 4
var description: String {
return "\(corners) 边"
}
}
let rectangle = SuperClass()
print("矩形: \(rectangle.description)")
class SubClass: SuperClass {
override init() { //重载构造器
super.init()
corners = 5
}
}
let subClass = SubClass()
print("五角型: \(subClass.description)")
La salida de ejecución del programa anterior es:
矩形: 4 边 五角型: 5 边
Ejemplo de inicializador designado y de conveniencia
El siguiente ejemplo mostrará en acción la herencia de inicializadores designados, de conveniencia y automáticos.
Define una jerarquía de clases que contiene dos clases, MainClass y SubClass, y demostrará cómo interactúan sus inicializadores.
class MainClass {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[匿名]")
}
}
let main = MainClass(name: "Ejemplo")
print("MainClass 名字为: \(main.name)")
let main2 = MainClass()
print("没有对应名字: \(main2.name)")
class SubClass: MainClass {
var count: Int
init(name: String, count: Int) {
self.count = count
super.init(name: name)
}
override convenience init(name: String) {
self.init(name: name, count: 1)
}
}
let sub = SubClass(name: "Ejemplo")
print("MainClass 名字为: \(sub.name)")
let sub2 = SubClass(name: "Ejemplo", count: 3)
print("count 变量: \(sub2.count)")
La salida de ejecución del programa anterior es:
MainClass 名字为: Ejemplo 没有对应名字: [匿名] MainClass 名字为: Ejemplo count 变量: 3
Inicializadores fallables de clases
Si el objeto de una clase, estructura o enumeración puede fallar durante su propio proceso de inicialización, define un inicializador fallable para él.
Las posibles causas de un fallo en la inicialización incluyen:
Pasar valores de parámetros no válidos.
Falta algún recurso externo requerido.
No se cumplen condiciones específicas.
Para manejar adecuadamente la situación en la que la construcción puede fallar.
Puedes añadir uno o más inicializadores fallables en la definición de una clase, estructura o enumeración. La sintaxis es añadir un signo de interrogación después de la palabra clave init (init?).
Ejemplo
En el siguiente ejemplo, se define una estructura llamada Animal, la cual tiene una propiedad constante llamada species de tipo String.
Además, la estructura define un inicializador fallable con un parámetro species de tipo String. Este inicializador fallable se utiliza para comprobar si el parámetro recibido es una cadena vacía; si es una cadena vacía, el inicializador fallable falla al construir el objeto; de lo contrario, tiene éxito.
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}
//通过该可失败构造器来构建一个Animal的对象,并检查其构建过程是否成功
// someCreature 的类型是 Animal? 而不是 Animal
let someCreature = Animal(species: "长颈鹿")
// 打印 "动物初始化为长颈鹿"
if let giraffe = someCreature {
print("动物初始化为\(giraffe.species)")
}
El resultado de ejecutar el programa anterior es:
El animal se inicializa como jirafa.
Inicializador fallable de tipos enumerados
Puedes obtener un miembro de enumeración específico mediante un inicializador fallable que tenga uno o más parámetros.
Ejemplo
En el siguiente ejemplo, se define un tipo enumerado llamado TemperatureUnit. Contiene tres posibles miembros de enumeración (Kelvin, Celsius y Fahrenheit) y un inicializador fallable que se utiliza para encontrar el miembro de enumeración correspondiente a un valor Character:
enum TemperatureUnit {
// 开尔文,摄氏,华氏
case Kelvin, Celsius, Fahrenheit
init?(symbol: Character) {
switch symbol {
case "K":
self = .Kelvin
case "C":
self = .Celsius
case "F":
self = .Fahrenheit
default:
return nil
}
}
}
let fahrenheitUnit = TemperatureUnit(symbol: "F")
if fahrenheitUnit != nil {
print("这是一个已定义的温度单位,所以初始化成功。")
}
let unknownUnit = TemperatureUnit(symbol: "X")
if unknownUnit == nil {
print("这不是一个已定义的温度单位,所以初始化失败。")
}
El resultado de ejecutar el programa anterior es:
Esta es una unidad de temperatura definida, por lo que la inicialización se realiza correctamente. Esta no es una unidad de temperatura definida, por lo que la inicialización falla.
Inicializador fallable de clases
Los inicializadores fallables de tipos de valor (como estructuras o enumeraciones) no tienen ninguna restricción sobre cuándo o dónde desencadenar el comportamiento de fallo de la construcción.
Sin embargo, los inicializadores fallables de una clase solo pueden desencadenar el fallo después de que todas las propiedades de la clase se hayan inicializado y después de que se hayan completado todas las llamadas de delegación entre inicializadores de la clase.
Ejemplo
En el siguiente ejemplo, se define una clase llamada StudRecord. Debido a que la propiedad studname es una constante, una vez que la clase StudRecord se construye con éxito, la propiedad studname seguramente tendrá un valor no nil.
class StudRecord {
let studname: String!
init?(studname: String) {
self.studname = studname
if studname.isEmpty { return nil }
}
}
if let stname = StudRecord(studname: "失败构造器") {
print("模块为 \(stname.studname)")
}
El resultado de ejecutar el programa anterior es:
El módulo es el constructor de fallos.
Sobrescribir un inicializador fallable
Al igual que con otros inicializadores, también puedes sobrescribir el inicializador fallable de la clase base con un inicializador fallable de la subclase.
O bien, también puedes sobrescribir un inicializador fallable de la clase base con un inicializador no fallable de la subclase.
Puedes sobrescribir un inicializador fallable con un inicializador no fallable, pero lo contrario no funciona.
Un inicializador no fallable nunca puede delegar en un inicializador fallable.
Ejemplo
El siguiente ejemplo describe inicializadores fallables y no fallables:
class Planet {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[No Planets]")
}
}
let plName = Planet(name: "Mercury")
print("行星的名字是: \(plName.name)")
let noplName = Planet()
print("没有这个名字的行星: \(noplName.name)")
class planets: Planet {
var count: Int
init(name: String, count: Int) {
self.count = count
super.init(name: name)
}
override convenience init(name: String) {
self.init(name: name, count: 1)
}
}
El resultado de ejecutar el programa anterior es:
行星的名字是: Mercury 没有这个名字的行星: [No Planets]
Inicializador fallable init!
Normalmente definimos un inicializador fallable agregando un signo de interrogación después de la palabra clave init (init?), pero también puedes definir un inicializador fallable agregando un signo de exclamación después de init (init!). A continuación, un ejemplo:
struct StudRecord {
let stname: String
init!(stname: String) {
if stname.isEmpty {return nil }
self.stname = stname
}
}
let stmark = StudRecord(stname: "Ejemplo")
if let name = stmark {
print("指定了学生名")
}
let blankname = StudRecord(stname: "")
if blankname == nil {
print("学生名为空")
}
El resultado de ejecutar el programa anterior es:
Se especificó el nombre del estudiante. El nombre del estudiante está vacío.Otras extensiones