Un objeto es un tipo de dato que encapsula datos y funcionalidad en la forma de propiedades y métodos, respectivamente. A diferencia de las estructuras, se almacenan por referencian, lo que significa que más de una variable puede referenciar al mismo objeto en memoria.
Definición de un objeto
Los objetos son definidos en "Clases", con la palabra clave class, y luego se crean instancias a partir de su definición.
class Person {
var name = "David"
var age = 10
func rename(_ newName: String) {
self.name = newName
}
}
let person1 = Person()
person1.name = "Luis"
person1.age = 11
person1.rename("Luis David")
print(person1.name) // "Luis David"
Notar que, a diferencia de las estructuras, no es necesario poner el modificador mutating frente a la firma del método para modificar alguna propiedad.
Métodos de tipo sobrescribible
Además de la palabra clave static para definir una propiedad y un método de tipo, se puede usar la palabra clave class para definir un método estático sobrescribible.
class Person {
var name = "David"
var age = 10
class func description() {
print("Almacena una persona")
}
}
Person.description() // "Almacena una persona"
Copia de estructuras (valor) y clases (referencia)
Las estructuras y enumeraciones pueden copiarse porque conforman el protocolo Copyable. Si se requiere crear valores únicos que no puedan ser copiados, se debe declarar la instancia como Noncopyable (i.e. ~Copyable).
Al trabajar con clases, una variable almacena la dirección de memoria donde está almacenado un objeto, en lugar del objeto en sí mismo. Por tanto, al crear una copia, se copia la referencia, no el objeto.
Para comparar si dos variables almacenan la misma dirección de memoria, se usan los operadores de identidad (=== y !==).
self como referencia hacia sí mismo
Un objeto se puede referenciar a sí mismo con la palabra clave self.
self ayuda a distinguir entre el nombre de un parámetro y el de una variable de instancia.
class Person {
var name = "David"
var age = 10
func rename(_ name: String) {
self.name = name
}
}
let person1 = Person()
person1.rename("Luis David")
print(person1.name) // "Luis David"
self como Metatipo
Tipo.self me devuelve una referencia hacia el tipo en sí mismo, no una instancia de él.
let reference = Int.self
let newValue = reference.init(20)
print(newValue) // 20
Los metatipos son usados para pasar referencias a los tipos de datos a métodos e inicializadores de otros tipos.
ARC
"Automatic Reference Counting" (ARC) elimina la instancia de una clase cuando no hay ninguna constante o variable referenciando esa posición de memoria.
Un ciclo de retención fuerte aparece cuando dos objetos tienen propiedades que se referencian mutuamente.
class Employee {
var department: Department?
}
class Department {
var employee: Employee?
}
var e: Employee? = Employee()
var d: Department? = Department()
e.department = d
d.employee = e // Se completa el ciclo de retención
e = nil // e retenido por d.employee
d = nil // d retenido por e.department
weak aplica a variables opcionales mientras que unowned son no opcionales.
Los closures también pueden crear referencias hacia otras variables. Si se necesita referenciar propiedades o métodos con self dentro de un closure, se puede cambiar la referencia por weak o unowned en el "capture list", antes de los parámetros.
Herencia
Una clase puede heredar las propiedades y métodos de otra clase, y agregar sus propios atributos y comportamiento. Una subclase puede sobrescribir un método de una súper clase, siempre que no sea privado.
class Employee {
var name = "Undefined"
var job = "Undefined"
func description() -> String {
"\(name), \(job)"
}
}
class OfficeEmployee: Employee {
var department = "Undefined"
override func description() -> String {
super.description() + ", \(department)"
}
}
let david = OfficeEmployee()
david.name = "David"
david.job = "Desarrollador"
david.department = "Home"
let description = david.description()
print(description) // David, Desarrollador, Home
Conversión de tipos ("Type casting")
Se puede referenciar a instancias de varios tipos de subclases, por medio de una superclase (abstracción) común.
class Vehicle {
var name = "Undefined"
}
class Car: Vehicle {
var wheels: Int = 4
}
class Airplane: Vehicle {
var wings: Int = 2
}
var list: [Vehicle] = [Car(), Airplane()]
Para comprobar el tipo de un objeto y hacer una conversión de tipo se puede usar los operadores is y as:
-
is: Retornatruesi el valor es del subtipo especificado. -
as: Convierte un valor de un tipo a otro, si es posible. Como la operación puede fallar,aspuede retornarnil, así que en realidad la sintaxis esas?yas!.
var cars = 0
var airplanes = 0
for vehicle in list {
if vehicle is Car {
cars += 1
let car = vehicle as! Car
car.wheels = 18 // Tractomula
} else if let airplane = vehicle as? Airplane {
airplane.wings = 4
airplanes += 1
}
}
Para asociar objetos que no hacen parte de la misma jerarquía se pueden usar los tipos de datos Any (para estructuras), AnyObject (para instancias de clases) y AnyClass (para tipos de clases).
class Vehicle {
var name = "Undefined"
}
class Car: Vehicle {
var wheels: Int = 4
}
class Animal {
var name = "Undefined"
var age = 0
}
var list: [AnyObject] = [Car(), Animal()]
Inicialización
Las clases, a diferencia de las estructuras, no proveen un inicializador por miembros. Las clases deben tener, por obligación, al menos un inicializador designado.
class Animal {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
let rocky = Animal(name: "Rocky", age: 13)
El inicializador designado de una subclase debe inicializar primero las propiedades de su propia clase y luego invocar el inicializador de su súperclase.
Desinicialización
Las clases tienen un método deinit que ejecuta instrucciones de último minuto antes de que el objeto sea eliminado de memoria.
class Animal {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
deinit {
print("Eliminando a \(name)")
}
}
var rocky: Animal? = Animal(name: "Rocky", age: 13)
rocky = nil // Eliminando a Rocky
Control de acceso y modificadores
Algunas palabras clave confieren un funcionamiento especial a ciertas entidades como clases, estructuras, métodos, entre otras.
-
final: Usado en una clase o método cuando se quiere que no sea sobrescrito. -
open: La entidad en cuestión es accesible por el módulo al que pertenece y otros módulos. Además, se puede sobrescribir tanto en módulo servidor como en los clientes. -
public: La entidad en cuestión es accesible por el módulo al que pertenece y otros módulos. NO se puede sobrescribir en los módulos clientes, pero sí en el módulo servidor. -
internal: La entidad en cuestión solo es accesible dentro del módulo al que pertenece. Este es el modo de acceso por defecto. -
fileprivate: La entidad en cuestión solo es accesible dentro del archivo donde fue declarada. -
private: La entidad en cuestión solo es accesible dentro del contexto en el que se creó. Por ejemplo, un atributo privado de una clase solo puede ser accedido por métodos de la misma. -
private(set): La entidad en cuestión solo puede ser modificada dentro del contexto en el que se creó. * Este modificador puede ser combinado con otros, por ejemplo,internal private(set): permite lectura dentro del módulo servidor, y escritura solo desde el contexto de definición, aunqueinternales redundante dado que es el modificador de acceso por defecto.
Top comments (0)