Difference between == and ===

!== and === are identity operators and are used to determine if two objects have the same reference.

Swift also provides two identity operators (=== and !==), which you use to test whether two object references both refer to the same object instance.

Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/us/jEUH0.l


In short:

== operator checks if their instance values are equal, "equal to"

=== operator checks if the references point the same instance, "identical to"

Long Answer:

Classes are reference types, it is possible for multiple constants and variables to refer to the same single instance of a class behind the scenes. Class references stay in Run Time Stack (RTS) and their instances stay in Heap area of Memory. When you control equality with == it means if their instances are equal to each other. It doesn't need to be same instance to be equal. For this you need to provide a equality criteria to your custom class. By default, custom classes and structures do not receive a default implementation of the equivalence operators, known as the “equal to” operator == and “not equal to” operator != . To do this your custom class needs to conform Equatable protocol and it's static func == (lhs:, rhs:) -> Bool function

Let's look at example:

class Person : Equatable {
    let ssn: Int
    let name: String

    init(ssn: Int, name: String) {
        self.ssn = ssn
        self.name = name
    }

    static func == (lhs: Person, rhs: Person) -> Bool {
        return lhs.ssn == rhs.ssn
    }
}

P.S.: Since ssn(social security number) is a unique number, you don't need to compare if their name are equal or not.

let person1 = Person(ssn: 5, name: "Bob")
let person2 = Person(ssn: 5, name: "Bob")

if person1 == person2 {
   print("the two instances are equal!")
}

Although person1 and person2 references point two different instances in Heap area, their instances are equal because their ssn numbers are equal. So the output will be the two instance are equal!

if person1 === person2 {
   //It does not enter here
} else {
   print("the two instances are not identical!")
}

=== operator checks if the references point the same instance, "identical to". Since person1 and person2 have two different instance in Heap area, they are not identical and the output the two instance are not identical!

let person3 = person1

P.S: Classes are reference types and person1's reference is copied to person3 with this assignment operation, thus both references point the same instance in Heap area.

if person3 === person1 {
   print("the two instances are identical!")
}

They are identical and the output will be the two instances are identical!


In both Objective-C and Swift, the == and != operators test for value equality for number values (e.g., NSInteger, NSUInteger, int, in Objective-C and Int, UInt, etc. in Swift). For objects (NSObject/NSNumber and subclasses in Objective-C and reference types in Swift), == and != test that the objects/reference types are the same identical thing -- i.e., same hash value -- or are not the same identical thing, respectively.

let a = NSObject()
let b = NSObject()
let c = a
a == b // false
a == c // true

Swift's identity equality operators, === and !==, check referential equality -- and thus, should probably be called the referential equality operators IMO.

a === b // false
a === c // true

It's also worth pointing out that custom reference types in Swift (that do not subclass a class that conforms to Equatable) do not automatically implement the equal to operators, but the identity equality operators still apply. Also, by implementing ==, != is automatically implemented.

class MyClass: Equatable {
  let myProperty: String

  init(s: String) {
    myProperty = s
  }
}

func ==(lhs: MyClass, rhs: MyClass) -> Bool {
  return lhs.myProperty == rhs.myProperty
}

let myClass1 = MyClass(s: "Hello")
let myClass2 = MyClass(s: "Hello")
myClass1 == myClass2 // true
myClass1 != myClass2 // false
myClass1 === myClass2 // false
myClass1 !== myClass2 // true

These equality operators are not implemented for other types such as structures in either language. However, custom operators can be created in Swift, which would, for example, enable you to create an operator to check equality of a CGPoint.

infix operator <==> { precedence 130 }
func <==> (lhs: CGPoint, rhs: CGPoint) -> Bool {
  return lhs.x == rhs.x && lhs.y == rhs.y
}

let point1 = CGPoint(x: 1.0, y: 1.0)
let point2 = CGPoint(x: 1.0, y: 1.0)
point1 <==> point2 // true

In swift 3 and above

=== (or !==)

  • Checks if the values are identical (both point to the same memory address).
  • Comparing reference types.
  • Like == in Obj-C (pointer equality).

== (or !=)

  • Checks if the values are the same.
  • Comparing value types.
  • Like the default isEqual: in Obj-C behavior.

Here I compare three instances (class is a reference type)

class Person {}

let person = Person()
let person2 = person
let person3 = Person()

person === person2 // true
person === person3 // false

There are subtleties with Swifts === that go beyond mere pointer arithmetics. While in Objective-C you were able to compare any two pointers (i.e. NSObject *) with == this is no longer true in Swift since types play a much greater role during compilation.

A Playground will give you

1 === 2                    // false
1 === 1                    // true
let one = 1                // 1
1 === one                  // compile error: Type 'Int' does not conform to protocol 'AnyObject'
1 === (one as AnyObject)   // true (surprisingly (to me at least))

With strings we will have to get used to this:

var st = "123"                                 // "123"
var ns = (st as NSString)                      // "123"
st == ns                                       // true, content equality
st === ns                                      // compile error
ns === (st as NSString)                        // false, new struct
ns === (st as AnyObject)                       // false, new struct
(st as NSString) === (st as NSString)          // false, new structs, bridging is not "free" (as in "lunch")
NSString(string:st) === NSString(string:st)    // false, new structs
var st1 = NSString(string:st)                  // "123"
var st2 = st1                                  // "123"
st1 === st2                                    // true
var st3 = (st as NSString)                     // "123"
st1 === st3                                    // false
(st as AnyObject) === (st as AnyObject)        // false

but then you can also have fun as follows:

var st4 = st             // "123"
st4 == st                // true
st4 += "5"               // "1235"
st4 == st                // false, not quite a reference, copy on write semantics

I am sure you can think of a lot more funny cases :-)

Update for Swift 3 (as suggested by the comment from Jakub Truhlář)

1===2                                    // Compiler error: binary operator '===' cannot be applied to two 'Int' operands
(1 as AnyObject) === (2 as AnyObject)    // false
let two = 2
(2 as AnyObject) === (two as AnyObject)  // false (rather unpleasant)
(2 as AnyObject) === (2 as AnyObject)    // false (this makes it clear that there are new objects being generated)

This looks a little more consistent with Type 'Int' does not conform to protocol 'AnyObject', however we then get

type(of:(1 as AnyObject))                // _SwiftTypePreservingNSNumber.Type

but the explicit conversion makes clear that there might be something going on. On the String-side of things NSString will still be available as long as we import Cocoa. Then we will have

var st = "123"                                 // "123"
var ns = (st as NSString)                      // "123"
st == ns                                       // Compile error with Fixit: 'NSString' is not implicitly convertible to 'String'; did you mean to use 'as' to explicitly convert?
st == ns as String                             // true, content equality
st === ns                                      // compile error: binary operator '===' cannot be applied to operands of type 'String' and 'NSString'
ns === (st as NSString)                        // false, new struct
ns === (st as AnyObject)                       // false, new struct
(st as NSString) === (st as NSString)          // false, new structs, bridging is not "free" (as in "lunch")
NSString(string:st) === NSString(string:st)    // false, new objects
var st1 = NSString(string:st)                  // "123"
var st2 = st1                                  // "123"
st1 === st2                                    // true
var st3 = (st as NSString)                     // "123"
st1 === st3                                    // false
(st as AnyObject) === (st as AnyObject)        // false

It is still confusing to have two String classes, but dropping the implicit conversion will probably make it a little more palpable.