swift 5: memory leaks and closures

While certainly nothing compared to conventional memory management, one frustrating aspect of finishing up your iOS app is discovering runaway memory issues.

One primary source is with respect to closures. From the Swift Programming Reference Manual:

Closures are self-contained blocks of functionality that can be passed around and used in your code…

Closures can capture and store references to any constants and variables from the context in which they are defined. This is known as closing over those constants and variables. Swift handles all of the memory management of capturing for you.

https://docs.swift.org/swift-book/LanguageGuide/Closures.html

This last bolded-statement is technically correct, but not without caution. Closures can capture and store references. The implication being a possible Automatic Referencing Counting Cycle. Put simply, Swift can inadvertently create a referencing loop between the object passed to the closure and the object calling the closure. Below is a diagram illustrating the point:

We’ve run into this issue when using SpriteKit and SKActions. Take for example, the following function:

func run(_ toPoint: CGPoint speed: TimeInterval) {
        let move = SKAction.move(to: toPoint, duration: speed)
        self.run(move) {
            self.jump()
        }
    }

In many cases, calling self.jump() will result in a memory leak given the strong reference. The solution to this memory leak:

func run(_ toPoint: CGPoint speed: TimeInterval) {
        let move = SKAction.move(to: toPoint, duration: speed)
        self.run(move) {
            [weak self] in
            guard let strongSelf = self else { return }
            strongSelf.jump()
        }
    }

And there you have it. A simple solution to a nagging issue we all face at some point. Hope this helps.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.