Swift – Copy On Write

En esta ocasión aprenderemos sobre una característica bien interesante del lenguaje Swift llamada Copy on Write (copia al escribir), y que se implementa sobre los tipos por valor.

A favor de lograr entender del todo lo que comentaremos aquí, me gustaría recomendar la lectura del artículo donde hablamos sobre los tipos por referencia y los tipos por valor en el lenguaje de programación Swift.

No obstante recordemos brevemente:

A diferencia de los tipos por referencias, donde dos instancias pueden compartir el mismo segmento de memoria. Los tipos por valor son inmutables por efecto, y cada vez que asignamos un valor o igualamos una variable con otra. Comienza todo un proceso de asignación e inicialización de memoria, que en dependencia del tamaño del dato puede consumir más o menos recursos de nuestro dispositivo.

¿Qué es el Copy on Write?

La característica (o comportamiento) Copy on Write es muy ingeniosa, y viene a reducir grandemente el costo en recursos al interactuar con arreglos o diccionarios.

La funcionalidad Copy on Write es bien sencilla de entender, y consiste en lo siguiente:

Al efectuar una asignación de un arreglo sobre otro, o de un diccionario a otro: no se asigna un nuevo espacio de memoria, se opta por compartir el mismo segmento de memoria. ¿Qué sentido tiene almacenar en memoria dos arreglos o diccionarios que son idénticos?

Luego, cuando este valor común (del arreglo o del diccionario) es modificado, es cuando realmente se copia el valor modificado a un nuevo segmento de memoria.

En palabras de C++ eso sería básicamente declarar un puntero a la dirección de memoria de la variable que ya tenemos, y de esta manera compartir el valor común que hasta ese momento tienen asignado dicho segmento de memoria, y al cual apuntan ambas variables.

Ejemplo

Veamos un ejemplo en código para entenderlo aún mejor:

//
// Función de ayuda
//

func address(of object: UnsafeRawPointer) -> String {
    
    let address = Int(bitPattern: object)

    return String(format: "%p", address)

} // address

// Inicio del código de ejemplo 👇

//
// Creamos el primer arreglo y le asignamos un valor
//

var firstArray = [10]

//
// Imprimimos la dirección de memoria de firstArray
//

print("1 -  firstArray: \(address(of: &firstArray))")

//
// Creamos el segundo arreglo igualándolo al primero
//

var secondArray = firstArray

//
// Imprimimos la dirección de memoria de secondArray
// que en este momento es la misma de firstArray
//

print("2 - secondArray: \(address(of: &secondArray))")

//
// Ahora mutamos el valor de firstArray agregándole un nuevo valor (5). Siendo ahora igual a [10, 5]
//

firstArray.append(5)

print("\n¡Arreglo modificado!\n")

//
// Veamos como han cambiado las direcciones de memoria, en este caso la de firstArray
//

print("1 -  firstArray: \(address(of: &firstArray))")
print("2 - secondArray: \(address(of: &secondArray))")

La salida en pantalla (en mi ordenador) del ejemplo sería la siguiente:

1 -  firstArray: 0x60000076baa0
2 - secondArray: 0x60000076baa0

¡Arreglo modificado!

1 -  firstArray: 0x60000074cd70
2 - secondArray: 0x60000076baa0

Beneficios

Como podemos observar en el ejemplo (solo imaginemos que es un arreglo de 100 elementos o más) luego de igualar secondArray con firstArray ambas variables comparten la misma dirección de memoria.

Nos hemos ahorrado (de momento) el consumo de recursos asociado al proceso de asignación de memoria.

Pero no termina aquí, si en este punto verificamos si ambos arreglo son idénticos mediante el operador de igualdad (==). Swift ni se molesta en ejecutar la comparación, ya que al ver que ambas variables comparten la misma dirección de memoria, pues es lógico asumir de que el valor almacenado será el mismo.

Una vez que modificamos el valor pasamos a trabajar con dos valores distintos, y por ende toma sentido diferenciar ambas variables y es necesaria la creación de un segundo segmento de memoria.

Conclusiones

No creo que haya mucho más que explicar, creo que el ejemplo de código ya se explica de por sí. No obstante ante cualquier duda ahí están los comentarios.

Solo aclarar (nuevamente) de que esta característica solo está disponible para los tipos Array y Dictionary.

☝️ Luego me dicen que lo probaron con un Int y nos les funciona. 🤷‍♂️

Tampoco se aplica sobre el resto de tipos de datos y tampoco para los nuestros propios. Aunque ya hemos visto como podemos solucionar esto último.

Falta aún mucho por aprender en nuestro camino a convertirnos en iOS Developer. Suscríbete a nuestra lista de correo y síguenos en nuestras redes sociales. Mantente al tanto de todas nuestras publicaciones.

Espero que todo cuanto se ha dicho aquí, de una forma u otra le haya servido de aprendizaje, de referencia, que haya valido su preciado tiempo.

Este artículo, al igual que el resto, será revisado con cierta frecuencia en pos de mantener un contenido de calidad y actualizado.

¡Cualquier duda o sugerencia, ya sea errores a corregir o ejemplos a añadir, será más que bienvenida, necesaria!

Deja una respuesta

Su dirección de correo electrónico no será publicada.

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Este sitio web utiliza cookies para mejorar su experiencia. Asumiremos que está de acuerdo con esto, pero puede optar por no participar si lo desea. Aceptar Leer Más

RECIBE CONTENIDO SIMILAR EN TU CORREO

RECIBE CONTENIDO SIMILAR EN TU CORREO

Suscríbete a nuestra lista de correo y mantente actualizado con las nuevas publicaciones.

Se ha suscrito correctamente!