Deep Copy and Shallow Copy - Swift
Reference and Value Types in Swift
In this post we are going to examine the differences between reference and value types. We’ll introduce both concepts, take a look at their strengths and weaknesses and examine how we can take advantage of them in Swift.
Reference Types
Reference type: a type that once initialized, when assigned to a variable or constant, or when passed to a function, returns a reference to the same existing instance.
A typical example of a reference type is an object. Once instantiated, when we either assign it or pass it as a value, we are actually assigning or passing around the reference to the original instance (i.e. its location in memory). Reference types assignment is said to have shallow copy semantics.
In Swift, objects are created using the class keyword.
Value Types
Value type: a type that creates a new instance (copy) when assigned to a variable or constant, or when passed to a function.
A typical example of a value type is a primitive type. Once instantiated, when we either assign it or pass it as a value, we are actually getting a copy of the original instance. Value types assignment is said to have deep copy semantics.
In Swift, value types can be defined using the struct keyword. Also, enumsand tuples can be value types.
The issue with Reference Type instances: implicit data sharing
In order to show a typical issue with reference types, let’s define a class to represent a point in a 2D space.
class PointClass {var x: Int = 0var y: Int = 0init(x: Int, y: Int) {self.x = xself.y = y}}
Now, what happens if we instantiate a PointClass object and assign it to another one?
var pointA = PointClass(x: 1, y: 3)var pointB = pointA
Because PointClass is a reference type, the last statement is actually assigning the reference to pointA to pointB. We can graphically represent the above scenario as follows:
In this situation, pointB and pointA share the same instance. Hence, any change to pointA will reflect on pointB and vice versa. This may be fine in many circumstances, but is also a very common source of subtle bugs.
One way to avoid this issue is to explicitly create a copy of the instance. Instead of just assigning pointA, we can manually create a copy and assign it:
var pointB = pointA.copy()
Now, pointB has its own separate reference and there will be no more shared data between pointA and pointB. This technique works fine but has a couple of disadvantages:
- explicitly calling copy() for each assignment introduces some overhead
- it is easy to forget to call copy() for each assignment
Value Type instances: no implicit sharing
When assigning value types, the compiler will automatically create (and return) a copy of the instance. Let’s see what happens if, instead of defining our 2D point as a class (reference type), we make it a struct (value type).
struct PointStruct {var x: Int = 0var y: Int = 0init(x: Int, y: Int) {self.x = xself.y = y}}
Now, we can create an instance of PointStruct and assign it to another one.
var pointA = PointStruct(x: 1, y: 3)var pointB = pointA
Because PointStruct is a value type, the last statement is creating a copy of pointA to be assigned to pointB. This makes the assignment safe as the two instances are distinct. We can graphically represent this situation as follows:
We can see that pointB has its own separate reference and there will be no shared data between pointA and pointB. This shows that by using value types we can easily make sure that all our instances are distinct and don’t share any data.
From a performance standpoint, using value types doesn’t add a significant overhead:
Copies Are Cheap
• Copying a primitive type (Int, Double, …) takes constant time
• Copying a struct, enum or tuple of value types takes constant time
Extensible data structures use copy-on-write
• Copying involves a fixed number of reference-counting operations
• This technique is used by many standard library types: String, Array, Set, Dictionary, …
In addition to the above, another performance benefit of value types is that they are stack allocated, which is more efficient than heap allocation (used for reference types). This makes access faster but comes with the drawback of having to drop support for inheritance.
It is important to point out that structs, enums and tuples are true value typesonly if all their properties are value types. If any of their properties is a reference type, we still could run into the implicit data sharing issues illustrated in the previous paragraph.
This comment has been removed by the author.
ReplyDeleteA huge percentage of the adult population suffers from edema, a condition in which their ankles and feet swell, making it difficult for them to walk comfortably. Some shoes don’t accommodate to edema and it is a real shame as everyone wants to be comfortable while looking nice extra wide women's shoes for swollen feet. However, shoe manufacturers have worked extra hard to provide specially designed footwear that can address swelling in the ankles and feet while offering comfort and relief. These shoes differ from conventional varieties.
ReplyDelete