Reference Equality vs Value Equality in Swift

Reference Equality vs Value Equality in Swift

In this blog post, we will explore the difference between reference equality and value equality in Swift, and how to use them correctly.

What is Reference Equality?

Reference equality is a way of comparing two objects based on their memory address. It means that two objects are equal if they refer to the same instance in memory. Reference equality is only applicable to reference types, such as classes, closures, and protocols.

To check for reference equality, we use the identity operators === and !==, which returns true or false depending on whether two references point to the same object or not.

For example:

class Person {
    var name: String

    init(name: String) {
        self.name = name
    }
}

let alice = Person(name: "Alice")
let bob = Person(name: "Bob")
let charlie = alice

alice === bob // false
alice === charlie // true

In this example, we create three variables of type Person, which is a class. We assign different instances of Person to alice and bob, but we assign the same instance as alice to charlie. Therefore, when we compare them using ===, we get false for alice === bob, but true for alice === charlie.

What is Value Equality?

Value equality is a way of comparing two objects based on their content. It means that two objects are equal if they have the same value or state. Value equality can be applied to both reference types and value types, such as structs, enums, tuples, arrays, dictionaries, sets, etc.

To check for value equality, we use the equal-to operator == and not-equal-to operator !=, which returns true or false depending on whether two values are equal or not.

However, unlike reference types, value types do not have a default implementation of value equality. We need to conform them to the protocol Equatable, which requires us to implement a static method called == that takes two values of our type and returns a boolean.

For example:

struct Point: Equatable {
    var x: Int
    var y: Int

    static func == (lhs: Point, rhs: Point) -> Bool {
        return lhs.x == rhs.x && lhs.y == rhs.y
    }
}

let p1 = Point(x: 1, y: 2)
let p2 = Point(x: 3, y: 4)
let p3 = Point(x: 1 ,y: 2)

p1 == p2 // false
p1 == p3 // true

In this example, we create a struct called Point, which represents a coordinate in a 2D plane. We conform it to Equatable and implement the == method that compares two points based on their x and y values. We then create three variables of type Point, with different or same values. When we compare them using ==, we get false for p1 == p2, but true for p1 == p3.

When to Use Reference Equality or Value Equality?

The choice between reference equality and value equality depends on the semantics of our types and what we want to achieve.

Generally speaking, reference equality is useful when we want to check if two objects are the same instance, or if they share some common state or behavior. For example, we might use reference equality to check if two views are identical, or if two delegates are the same object.

On the other hand, value equality is useful when we want to check if two objects have the same content, or if they represent the same concept or data. For example, we might use value equality to check if two strings have the same characters, or if two dates are equal.

However, there are some cases where both reference equality and value equality can be meaningful for the same type. For example, consider an array of integers:

let array1 = [1, 2 ,3]
let array2 = [1 ,2 ,3]
let array3 = array1

array1 === array2 // false
array1 === array3 // true
array1 == array2 // true
array1 == array3 // true

In this case, we can use reference equality to check if two arrays are the same instance in memory, or value equality to check if two arrays have the same elements in the same order. Both comparisons can be valid depending on our context and intention.

Conclusion

We saw that reference equality is checked using === and !==, and is only applicable to reference types. We also saw that value equality is checked using == and !=, and can be applied to both reference types and value types, but requires us to conform to Equatable for value types.

Finally, we discussed when to use reference equality or value equality depending on the semantics of our types and what we want to achieve.

I hope you enjoyed this blog post and learned something new. If you have any questions or feedback, please leave a comment below. Thank you for reading!