This sounds like a case of abandoned memory, not a memory leak. The Xcode memory debugger can automatically identify leaks, but not abandoned memory.
In ARC, an abandoned allocation is an allocation accidentally strongly referenced after its expected lifetime. You can typically address this by using weak or unowned references instead of strong references where appropriate.
For example, consider the following:
import Foundation
let notificationName = NSNotification.Name("SomeNotification")
class IsStronglyCaptured {
var token: NSObjectProtocol? = nil
init() {
self.token = NotificationCenter.default.addObserver(forName: notificationName, object: nil, queue: nil) { notification in
print(self)
}
}
deinit {
print("IsStronglyCaptured is being deinitialized")
if let token {
NotificationCenter.default.removeObserver(token, name: notificationName, object: nil)
}
}
}
class IsWeaklyCaptured {
var token: NSObjectProtocol? = nil
init() {
self.token = NotificationCenter.default.addObserver(forName: notificationName, object: nil, queue: nil) { [weak self] notification in
print(String(describing: self))
}
}
deinit {
print("IsWeaklyCaptured is being deinitialized")
if let token {
NotificationCenter.default.removeObserver(token, name: notificationName, object: nil)
}
}
}
_ = IsStronglyCaptured()
_ = IsWeaklyCaptured()
In this code IsStronglyCaptured
and IsWeaklyCaptured
instances register for notifications from NotificationCenter. In the callbacks IsStronglyCaptured
is captured strongly, while IsWeaklyCaptured
is captured weakly because it uses a capture list of [weak self]
. The IsStronglyCaptured
instance will persist indefinitely because of the strong capture, while the IsWeaklyCaptured
instance will be deallocated almost immediately.
The program's output confirms this, showing that the IsWeaklyCaptured
instance's deinitializer runs but the IsStronglyCaptured
instance's doesn't:
IsWeaklyCaptured is being denitialized
The Xcode memory debugger won't automatically point out such references, but it can be used to manually search for them. If you can find an abandoned allocation in the memory debugger you can use the memory debugger to see what references (and potentially retains) that allocation.
When analyzing the above example after its final line executes, the Xcode memory debugger shows that the IsStronglyCaptured
instance still exists and is referenced by NotificationCenter-related allocations, which indicates that NotificationCenter may be retaining the abandoned allocation.