Value types are faster to work with since they operate out of the stacks and copying a value is cheap since it happens in a constant time. Values also help us achieve predictable behaviour and isolation whereas, on the other hand Reference types give shared access to Memory locations and dynamic storage.
Since reference types share the address for same memory if one of the clients changes the value in that memory location then all the clients see the change and this can work like a double edged sword.
It is because of the above mentioned reason, references can cause surprising behaviour and as a developer you might not want that.
In this article we are going to discuss Swift 5 Value and Reference types and see how are they handled.
Swift only has a total of six types: These consist of the four Named types or Nominal types:
- protocol : Value type (but can be Reference types)
- struct : Value type
- enum : Value type
- class : Reference type
and two Unnamed/Compound types:
- tuple : Value type
- function : Reference type
structs, enums and tuples have value semantics whereas classes and functions have reference semantics. We can combine value and reference semantics together to give us the best of both worlds.
Difference between value types and reference types
In this section we will demonstrate the difference between value types and reference types and then discuss an example exploiting reference types to make a testable design.
In the first example we have TestValue as a value and TestReference as a reference type, as follows:
This creates a value and reference version of the Test. Let’s now look at the behaviour of the value type:
Next, let’s look at the behaviour of the reference type:
As we mentioned earlier, reference types work on the same memory addresses because of which changing one client reflects the change in other client. In large complicated programs this interaction can result in confusing behaviour. It is because of this reason managing the state of the program becomes hard when we work extensively with reference types and yet this behaviour can be used for good. For example, we can use it to implement “action at a distant testing”.
Action at a distance is an anti-pattern (a recognised common error) in computer science in which behaviour in one part of a program varies wildly based on difficult or impossible to identify operations in another part of the program.
Use of Reference types in Testing Value types
Let’s have a look at the following struct:
You can see that it is really hard to test the functionality in this struct. Now let’s try to fix the issues in the above code by adding a mock and try to make it more testable.
First we will add a protocol called CountKeeper and then a struct RealTimeCounter to provide default implementation to the protocol as follows:
Now we can go ahead an inject this CountKeeper protocol dependency to our Counter to make it more testable as follows:
Finally, let’s create a mock object that is a reference type as follows:
Now we can write a number of tests by making use of this reference type mock object because this mock object lets us reach inside the Test class and change it from the outside and simultaneously we can be rest assured that such a behaviour will not be exhibited in our production code since the CountKeeper for production code is a struct. You can see the result of multiple test cases in the screenshot below:
For other updates you can follow me on Twitter on my twitter handle @NavRudraSambyal
Thanks for reading, please share it if you found it useful 🙂