0
0
Swiftprogramming~15 mins

Identity operators (=== and !==) in Swift - Deep Dive

Choose your learning style9 modes available
Overview - Identity operators (=== and !==)
What is it?
Identity operators in Swift are special symbols (=== and !==) used to check if two variables point to the exact same object in memory. Unlike equality operators that compare values, identity operators check if both variables refer to the same instance. This is important when working with classes, which are reference types in Swift.
Why it matters
Without identity operators, you could only check if two objects look the same, but not if they are actually the same object. This can cause bugs when you want to know if two variables share the same data or state. Identity operators help manage memory and object references safely and clearly.
Where it fits
Before learning identity operators, you should understand variables, classes, and the difference between value types and reference types in Swift. After this, you can learn about memory management, object lifecycle, and advanced topics like closures capturing references.
Mental Model
Core Idea
Identity operators check if two variables point to the exact same object in memory, not just if their contents are equal.
Think of it like...
Imagine two remote controls for the same TV. Equality is like checking if both remotes have the same buttons and labels. Identity is like checking if both remotes are literally the same physical remote in your hand.
┌───────────────┐       ┌───────────────┐
│ Variable A    │──────▶│ Object in     │
│               │       │ memory (TV)   │
└───────────────┘       └───────────────┘

┌───────────────┐       ┌───────────────┐
│ Variable B    │──────▶│ Object in     │
│               │       │ memory (TV)   │
└───────────────┘       └───────────────┘

If both arrows point to the same box, A === B is true.
Build-Up - 6 Steps
1
FoundationUnderstanding Reference Types
🤔
Concept: Classes in Swift create reference types, meaning variables hold references to objects, not the objects themselves.
In Swift, when you create a class instance and assign it to a variable, the variable stores a reference (like an address) to the object in memory. Multiple variables can refer to the same object.
Result
Variables can share the same object, so changing the object through one variable affects all references.
Understanding that classes are reference types is key to knowing why identity operators exist and when to use them.
2
FoundationDifference Between Equality and Identity
🤔
Concept: Equality (==) checks if two objects have the same value, while identity (===) checks if they are the same object.
For example, two different instances of a class can have identical properties but be different objects. Equality compares properties; identity compares memory references.
Result
You can have two objects that are equal but not identical, or identical and therefore equal.
Knowing this difference prevents confusion when comparing objects and helps choose the right operator.
3
IntermediateUsing === and !== Operators
🤔Before reading on: do you think === compares values or memory addresses? Commit to your answer.
Concept: The === operator returns true if two variables refer to the same object; !== returns true if they do not.
Example: class Person {} let a = Person() let b = a let c = Person() print(a === b) // true print(a === c) // false print(a !== c) // true
Result
The output shows which variables point to the same object and which do not.
Understanding how to use these operators helps you control object identity checks explicitly.
4
IntermediateIdentity Operators with Optional Types
🤔Before reading on: Can you use === to compare an optional and a non-optional class instance? Commit to your answer.
Concept: You can use identity operators to compare optional class instances safely, but you must unwrap optionals or use optional chaining.
Example: class Car {} var car1: Car? = Car() var car2: Car? = car1 if car1 === car2 { print("Same car") } // Using optional chaining if car1 === car2 { print("Same car") }
Result
The code safely checks if optional variables refer to the same object without crashing.
Knowing how identity operators work with optionals prevents runtime errors and improves code safety.
5
AdvancedIdentity Checks in Collections
🤔Before reading on: Do you think identity operators can be used to find objects in arrays? Commit to your answer.
Concept: You can use identity operators to find if a collection contains a specific object instance, not just equal values.
Example: class Book {} let book1 = Book() let book2 = Book() let library = [book1, book2] if library.contains(where: { $0 === book1 }) { print("Found book1") }
Result
The output confirms the presence of the exact object in the collection.
Using identity operators in collections helps manage references and avoid duplicates of the same object.
6
ExpertWhy Identity Operators Are Reference-Safe
🤔Before reading on: Do you think identity operators compare object contents or references internally? Commit to your answer.
Concept: Identity operators compare memory addresses directly, which is faster and safer than comparing contents for reference types.
Swift uses pointer comparison under the hood for === and !==, ensuring that identity checks are efficient and reliable even if objects have complex or mutable properties.
Result
Identity checks are constant time operations that do not depend on object size or complexity.
Understanding the pointer-level comparison explains why identity operators are preferred for reference equality and helps optimize performance.
Under the Hood
Swift stores class instances in memory and variables hold references (pointers) to these memory locations. The identity operators === and !== compare these pointers directly to check if two variables point to the same memory address. This is a simple integer comparison at the machine level, making it very fast and reliable.
Why designed this way?
Swift separates value equality and reference identity to avoid confusion and bugs. Comparing pointers directly is efficient and avoids the overhead of deep equality checks. This design follows common patterns in many programming languages to clearly distinguish between object identity and value equality.
┌───────────────┐       ┌───────────────┐
│ Variable A    │──────▶│ Memory Address │
│ (reference)   │       │ 0x1000        │
└───────────────┘       └───────────────┘

┌───────────────┐       ┌───────────────┐
│ Variable B    │──────▶│ Memory Address │
│ (reference)   │       │ 0x1000        │
└───────────────┘       └───────────────┘

Identity check: Compare 0x1000 == 0x1000 → true
Myth Busters - 4 Common Misconceptions
Quick: Does a === b mean a == b always? Commit to yes or no.
Common Belief:If two objects are identical (===), they must also be equal (==).
Tap to reveal reality
Reality:While identical objects are usually equal, equality can be customized and may not always hold true even if two variables point to the same object.
Why it matters:Assuming identity implies equality can cause logic errors when equality is overridden or when objects have mutable state.
Quick: Can === be used with structs? Commit to yes or no.
Common Belief:Identity operators === and !== can be used with any type, including structs.
Tap to reveal reality
Reality:Identity operators only work with class instances (reference types). Structs are value types and do not have identity.
Why it matters:Using identity operators on value types is a compile-time error, so misunderstanding this leads to confusion and wasted time.
Quick: Does using === check if two variables have the same content? Commit to yes or no.
Common Belief:Using === checks if two variables have the same content or data.
Tap to reveal reality
Reality:=== only checks if two variables point to the exact same object, not if their contents are equal.
Why it matters:Confusing identity with equality can cause bugs where two objects look the same but are treated as different or vice versa.
Quick: Can identity operators cause performance issues? Commit to yes or no.
Common Belief:Using identity operators is slow because it compares entire objects.
Tap to reveal reality
Reality:Identity operators compare memory addresses directly, which is very fast and constant time.
Why it matters:Misunderstanding performance can lead to avoiding identity checks unnecessarily or using expensive equality checks instead.
Expert Zone
1
Identity operators do not trigger any custom equality logic, so they are immune to bugs caused by overridden equality methods.
2
Using identity operators with weak references requires care because the referenced object might be nil, affecting the comparison.
3
Stacked identity checks in complex object graphs can reveal subtle bugs in reference sharing and ownership.
When NOT to use
Avoid identity operators when you want to compare the actual content or state of objects; use equality operators (==) instead. For value types like structs or enums, identity operators do not apply; use value equality. When working with protocols or value semantics, identity checks are not meaningful.
Production Patterns
In real-world Swift apps, identity operators are used to detect if two variables share the same instance, such as in caching, singleton patterns, or managing UI elements. They help avoid duplicating objects and ensure consistent state management across references.
Connections
Pointer comparison in C/C++
Identity operators in Swift are similar to pointer comparisons in C/C++, both check if two pointers refer to the same memory address.
Understanding pointer comparison in lower-level languages helps grasp how identity operators work efficiently at the memory level.
Reference equality in Java
Swift's identity operators === and !== correspond to Java's == operator for objects, both check reference equality.
Knowing Java's reference equality clarifies why Swift separates identity and equality operators distinctly.
Philosophy of Identity
The concept of identity in programming parallels philosophical questions about what makes an object the same over time.
Recognizing this connection deepens understanding of why identity matters beyond code, influencing how we think about sameness and difference.
Common Pitfalls
#1Confusing identity with equality and using === to compare values.
Wrong approach:let a = "hello" let b = "hello" if a === b { print("Same") }
Correct approach:let a = "hello" let b = "hello" if a == b { print("Same") }
Root cause:Misunderstanding that === only works with class instances, not value types like strings.
#2Using identity operators on structs causing compile errors.
Wrong approach:struct Point { var x: Int; var y: Int } let p1 = Point(x: 1, y: 2) let p2 = Point(x: 1, y: 2) if p1 === p2 { print("Same") }
Correct approach:struct Point { var x: Int; var y: Int } let p1 = Point(x: 1, y: 2) let p2 = Point(x: 1, y: 2) if p1 == p2 { print("Same") }
Root cause:Not knowing identity operators only apply to reference types, not value types.
#3Assuming two variables are different objects because === returns false, ignoring value equality.
Wrong approach:class Cat { var name: String init(name: String) { self.name = name } } let cat1 = Cat(name: "Mittens") let cat2 = Cat(name: "Mittens") if cat1 !== cat2 { print("Different") }
Correct approach:class Cat: Equatable { var name: String init(name: String) { self.name = name } static func == (lhs: Cat, rhs: Cat) -> Bool { return lhs.name == rhs.name } } let cat1 = Cat(name: "Mittens") let cat2 = Cat(name: "Mittens") if cat1 == cat2 { print("Equal") }
Root cause:Confusing identity with equality and not implementing value equality.
Key Takeaways
Identity operators === and !== check if two variables point to the exact same object in memory, not if their contents are equal.
They only work with class instances (reference types), not with value types like structs or enums.
Using identity operators helps manage object references safely and efficiently, especially in complex programs with shared data.
Confusing identity with equality is a common source of bugs; always choose the right operator for your comparison needs.
Understanding the memory-level pointer comparison behind identity operators explains their speed and reliability.