🚀 Exciting News: Introducing DebugSwift’s Leaked View and ViewController Detector! 🕵️‍♂️

Gois
4 min readMay 29, 2024

--

Find leaked UIViews and UIViewControllers in real time in your UIKit app without even looking for them!

Tired of hunting down memory leaks in your iOS or tvOS apps? DebugSwift’s latest feature, LeakedViewControllerDetector, is here to save the day. 🛠️

No more sifting through code to find those elusive memory leaks. This feature swiftly identifies leaked Views and ViewControllers in any UIKit app! And the best part? It requires minimal changes to your existing code. Set it up and forget about it. đź’ˇ

Key Features:

  • Automatic Detection: Identifies when a UIView or UIViewController fails to deinitialize properly.
  • Instant Alerts: Warns you with a dialog as soon as a leak is detected (in debug builds).
  • Works in Release Builds: Logs leak warnings to tools like Crashlytics.
  • Easy Setup: Minimal to no code changes required.
  • Efficient and Fast: Alerts with screenshots when a ViewController doesn’t deinit.

Setup

You don't need do anything, the DebugSwift collects de memory automatically!
But, if you wan't send to some crashlytics, try it:

Collect Memory Leaks

DebugSwift.Performance.LeakDetector.onDetect { data in
// If you want to send data to some analytics

print(data.message) // Retuns the name of the class and the error
print(data.controller) // If is an controller leak
print(data.view) // If is an view leak
print(data.isDeallocation) // If is an deallocation of leak (good for false/positive)
}

10 Common Causes of Memory Leaks in iOS Development and How to Avoid Them

Memory leaks can significantly impact the performance and reliability of iOS applications. Understanding and addressing the common causes of these leaks can lead to more efficient and stable code. Here are ten typical scenarios where memory leaks might occur in iOS development, along with strategies to prevent them.

1. Referencing Self in Callbacks

Referencing self inside a callback often leads to memory leaks. For example:

DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
print(self)
}

If the view controller is closed before the 10 seconds elapse, it will remain in memory until the callback executes. This can be avoided by using [weak self]:

DispatchQueue.main.asyncAfter(deadline: .now() + 10) { [weak self] in
guard let self = self else { return }
print(self)
}

2. NotificationCenter Observer Callback

Using `[weak self]` is also crucial when adding observers to `NotificationCenter`:

NotificationCenter.default.addObserver(
forName: NSNotification.Name(
"SomeNotification"
),
object: nil,
queue: nil
) { [weak self] notification in
print(self)
}

Alternatively, using a selector prevents memory leaks:

NotificationCenter.default.addObserver(
self,
selector: #selector(
someMethod
),
name: NSNotification.Name(
"SomeNotification"
),
object: nil
)

3. Using Non-Weak Delegates

Always declare delegates as weak to avoid memory leaks:

weak var delegate: MyViewControllerDelegate?

4. ViewController PresentationController Delegate

Setting the PresentationController delegate can cause leaks if not done correctly. Avoid setting it directly if the view controller is a child of a parent, like a NavigationController:

self.presentingViewController?.presentedViewController?.presentationController?.delegate = self

5. NavigationController PresentationController Delegate

Setting the `presentationController` delegate inside `viewDidDisappear` can cause leaks:

override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
navigationController?.presentationController?.delegate = nil
}

6. UIAlertController Action Callback

When referencing an alert inside one of its actions, use unowned or weak:

let alert = UIAlertController(title: "Retain test", message: nil, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Unowned", style: .default) { [unowned alert] action in
print(alert)
})
alert.addAction(UIAlertAction(title: "Weak", style: .default) { [weak alert] action in
print(alert!) // explicitly unwrapping weak alert
})
self.present(alert, animated: true)

7. Breakpoints and Debug View Hierarchy

Hitting breakpoints or using the view hierarchy debugger can sometimes lead to memory leaks. While this is less common, it’s important to be aware of the potential for leaks in these scenarios.

8. SplitViewControllers

On iPhones, using a SplitViewController can cause detail view controllers to stay in memory until a new one is presented. This is a known behavior and not strictly a memory leak, but it can lead to related issues if not managed properly.

9. ScrollView Inertia

Closing a view controller while a ScrollView (such as a TableView) is still scrolling can cause it to remain in memory temporarily. This is unavoidable but usually resolves itself within a second.

10. UITextView URL Preview

In iOS, UITextView can cause memory leaks when link detection is enabled. Subclassing UITextView and overriding removeFromSuperview() can fix this in iOS 17:

class CustomTextView: UITextView {
override func removeFromSuperview() {
let wasSelectable = isSelectable
isSelectable = false
super.removeFromSuperview()
isSelectable = wasSelectable
}
}

Conclusion

Memory leaks can be subtle and challenging to diagnose. By understanding common causes and applying best practices such as using weak references and careful observer management, you can significantly reduce the likelihood of memory leaks in your iOS applications. Happy coding!

--

--

Gois

 iOS Developer | Student | Researcher | Innovation Enthusiast.