Value types vs Reference types in swift (advanced)

ref – https://www.raywenderlich.com/112029/reference-value-types-in-swift-part-2

Mixing Value and Reference Types

You’ll often run into situations where reference types need to contain value types, and vice versa. This can easily complicate the expected semantics of the object.
To see some of these complications, you’ll look at an example of each scenario.

Reference Types Containing Value Type Properties

It’s quite common for a reference type to contain value types. An example would be a Person class where identity matters, that stores an Address structure where equality matters.
To see how this may look, replace the contents of your playground with the following basic implementation of an address:

All properties of Address together form a unique physical address of a building in the real world. The properties are all value types represented by String; the validation logic has been left out to keep things simple.

Next, add the following code to the bottom of your playground:

This mixing of types makes perfect sense in this scenario. Each class instance has its own value type property instances that aren’t shared. There’s no risk of two different people sharing and unexpectedly changing the address of the other person.
To verify this behavior, add the following to the end of your playground

Value Types Containing Reference Type Properties

Add the following code to your playground to demonstrate a value type containing a reference type:

Each copy of Bill is a unique copy of the data, but the billedTo Person object will be shared by numerous Bill instances. This simply does not make sense in maintaining the value semantics of your objects.

Using Copy-on-Write Computed Properties

Swift can give you the ability for your value type to spawn its own reference type. That way, when other objects wants to reference your value type’s reference object, it won’t simply point to it…as that would make one data object be changed by 2 or more references.

When other objects point to your value type’s reference object, your value type will spawn (create another instance) of the reference object for others. That way, your value type will have its own reference object with its respective address, and other object would have their reference object with its respective address.

1) declare a reference type
2) create a property (billedToForRead) to get the current reference
3) create another property (billedToCreateNewInstance) which re-assigns the reference to a new heap allocated Person object. When we create a new object on the heap, our property billedTo will re-point to that object. Thus, billedToCreateNewInstance will return a new object with a new address.

In Swift, we need to use the word mutating in order to let the value type know that its property will be changing.

If you can guarantee that your caller will use your structure exactly as you meant, this approach would solve your issue. In a perfect world, your caller would always use billedToForRead to get data from your reference and billedToCreateNewInstance to make a change to the reference.

However, callers will also make some mistakes in calling your API properties. Hence, what we can do is to hide our properties from the outside and create methods to

Defensive Mutating Methods

So as mentioned, we’ll hide the two new properties from the outside.

1) You made both computed properties private so that callers can’t access the properties directly.

2) You also added methods to mutate the Person reference with a new name or address. This makes it impossible for someone else to use it incorrectly, since you’re hiding the underlying billedTo property.

Also, take note that “Cannot use mutating member on immutable value”.

for example:

A More Efficient Copy-on-Write

There is only one small problem. Whenever we want to change the Person object’s attributes, we ALWAYS instantiate a Person object:

As you can see, whenever we get the billedTo attribute, we always return a newly instantiated object.

If another Bill object references this Person object, then yes, we should instantiate a new Person object for our own Bill object to reference, and change data with.

However, what if the Person object has no other references on it? ..and is only referenced by self?
In that case, we do not need to instantiate it. We simply use it as it, and change the attributes.

Hence here is what’s happening:

A Person object with “Ricky” (100c01fa0) is allocated on the heap. It is passed into the Bill object (100410438) to be initialized. This initialization is really about the Bill object creating its own instance of Person object (100c01fe0), and setting all its data the same as the passed in “Ricky” Person object. The original “Ricky” Person object created outside is not pointed to, nor affected.

copy-on-write-1-2

We then want to change the data of our Bill object to Ivan. We do this through the property, and evaluate whether the Person object 100c01fe0 has any other references on it. Uniquely Referenced means it is referenced only by 1 object.

Thus, we see that IT IS only referenced once, by our Bill object. Thus, we don’t create an instance and just return the Person object. We then proceed to change the data.

copy-on-write-3

Then say we have another Bill object reference our Person (100520008). Thus, we now have two references pointing to our Person object

copy-on-write-4

Because we have 2 references on the Person object, isKnownUniquelyReferenced will fail, and we create another Person object. That way, we get our own unique instance of Person.

copy-on-write-5