What is the memory management mechanism in Swift? What is ARC (Automatic Reference Counting)? How to avoid retain cycles?
Swift uses Automatic Reference Counting (ARC) to automatically manage application memory. ARC tracks and manages the memory used by your application, automatically freeing memory when instances are no longer needed.
How ARC Works:
- Each time a new reference to a class instance is created, ARC increases the instance's reference count
- When a reference is removed, ARC decreases the reference count
- When the reference count reaches zero, ARC frees the instance's memory
- ARC only applies to class type instances; structs and enums are value types and don't participate in reference counting
Strong Reference Cycles:
- Two or more class instances hold strong references to each other, causing reference counts to never reach zero
- Common scenarios: mutual references between classes, closures capturing class instances
- Example:
swift
class Person { var apartment: Apartment? } class Apartment { var tenant: Person? } let person = Person() let apartment = Apartment() person.apartment = apartment apartment.tenant = person
Methods to Resolve Retain Cycles:
-
Weak References:
- Declared using
weakkeyword - Does not increase reference count
- Weak references automatically become nil when the referenced object is deallocated
- Must be declared as optional type
- Suitable when the reference can be nil
- Example:
swift
class Apartment { weak var tenant: Person? }
- Declared using
-
Unowned References:
- Declared using
unownedkeyword - Does not increase reference count
- Unowned references don't automatically become nil when the referenced object is deallocated
- Cannot be declared as optional type
- Suitable when the referenced object has a longer lifetime
- Example:
swift
class Customer { let creditCard: CreditCard init(creditCard: CreditCard) { self.creditCard = creditCard } } class CreditCard { unowned let customer: Customer }
- Declared using
-
Retain Cycles in Closures:
- Use capture lists
[weak self]or[unowned self] - Example:
swift
class HTMLElement { let name: String lazy var asHTML: () -> String = { [weak self] in guard let self = self else { return "" } return "<\(self.name)>" } }
- Use capture lists
Best Practices:
- Use
weakorunownedin class properties to avoid strong reference cycles - Use capture lists in closures to handle retain cycles
- Use
weakwhen the reference can be nil - Use
unownedwhen the referenced object has a longer lifetime - Use Instruments tool to detect memory leaks