Swift: Reference Equality and Value Equality For Classes ​

There are two ways for testing the equality of class instances: reference equality and value equality. In this post we will discuss both of them.

Hint: This post is using Swift 4

The Identity Operator ===

By using the identity operator ===  you can compare the references pointing to the instances of a class. If these references are pointing to the same instance, the operator returns true. Take a look at the following example:

Class TestClassA { }

let test1 = TestClassA()
let test2 = TestClassA()
let test3 = test1

print("test1 and test2 pointing to the same instance: \(test1===test2)") //false
print("test1 and test3 pointing to the same instance: \(test1===test3)") //true

As you can see, test1 and test3 are pointing to the same instance, whereas test1 and test2 are not pointing to the same instance.

You can also check for inequality by using the !== operator:

print("test1 and test3 are not pointing to the same instances: \(test1!==test3)" //true

Note that the identity operator is not available for enums, structs and primitive data types. This makes sense because in these cases it’s not possible that two values are pointing to the same instance.

The equality operator ==

The equality operator on the other hand works differently. It compares whether two instances are equal. This doesn’t sound very concrete and in fact it’s not defined what that means. Instead, each class has to define what equality means for it. If the class doesn’t do this, it’s not possible to use the equality operator:

Class TestClassA { }

let test1 = TestClassA()
let test2 = TestClassA()

print("test1 is equal to test2 \(test1==test2)") //Compiler error

The last line produces a compiler error. Instead, you have to implement the Equatable protocol:

class TestClassA: Equatable {

     let a: Int

     init(a:Int) {
          self.a = a
     }

     public static func == (lhs: TestClassA, rhs: TestClassA) -> Bool {
          return lhs.a == rhs.a
     }

}

let test1 = TestClassA(a:5)
let test2 = TestClassA(a:6)
let test3 = TestClassA(a:5)

print("test1 is equal to test2 \(test1==test2)") //false
print("test1 is equal to test3 \(test1==test3)") //true

As you can see, in this example two instances of TestClass are equal if the properties a  are equal.

The comparable protocol

It’s not only possible to define the value equality for two instances of a class but an order as well. For that, you have to implement the Comparable protocol. It extends the Equatable protocol, so that the == function has to be implemented as explained in the last section. In addition, the < function also has to be implemented:

class TestClassA: Equatable, Comparable {

     let a: Int

     init(a:Int) {
          self.a = a
     }

     public static func < (lhs: TestClassA, rhs: TestClassA) -> Bool {
          return lhs.a < rhs.a
     }

     public static func == (lhs: TestClassA, rhs: TestClassA) -> Bool {
          return lhs.a == rhs.a
     }

}

let test1 = TestClassA(a:5)
let test2 = TestClassA(a:6)
let test3 = TestClassA(a:5)

print("test1 < test2 \(test1<test2)") //true
print("test1 < test3 \(test1<test3)") //false
print("test1 <= test3 \(test1<=test3)") //true
print("test1 > test3 \(test1>test2)") //false

As you can see, it’s very straight forward.

Title image: @ MShipphoto / shutterstock.com