Xavier L.
Xavier L.

Reputation: 747

Accessing Singletons in a Closure = Memory Leak?

Does accessing a singleton within a closure cause a retain cycle?

Specifically something like this example:

class TheSingleton
{
    static let shared = TheSingleton() //THE SINGLETON

    enum Temperature              //An associated enum
    {
        case cold, hot
    }

    var currentTemp: Temperature? //A non-class-type variable
    var aPicture: UIImage?        //A class-type variable

    func giveMeFive() -> Int      //A function
    {
         return 5
    }

    //Pay attention to this
    func doSomething(onDone: @escaping (Int) -> ())
    {
         OtherSVC.upload("Mr. Server, do async stuff plz") { (inImage) in
             TheSingleton.shared.currentTemp = .cold
             TheSingleton.shared.aPicture = inImage
             onDone(TheSingleton.shared.giveMeFive())
         }
    }
}
//Fire the thing
TheSingleton.shared.doSomething { _ in}

If so, I don't really know how to write a capture list for this...

[weak TheSingleton.shared] (inImage) in

You can't do that ^

I included three cases because maybe the type of data matters? I think I'm missing some fundamentals on capture lists and closure retain cycles.

All I know is whenever you access something outside of a closure's curly braces, you have to unown/weak it if it's a class-type object. That's because closures create strong references by default.

I thought I could be cheeky and get around retain cycles by calling the entire singleton in closures, but I'm probably being dumb by turning a blind eye.

Would a solution be to do something like:

var updateDis = TheSingleton.shared.aPicture
OtherSVC.upload("ugh this is lame, and more work") { [unowned updateDis] inPic in 
    updateDis = inPic
}

?

Upvotes: 3

Views: 1325

Answers (1)

Sweeper
Sweeper

Reputation: 272760

Since you are writing a singleton, TheSingleton.shared is pretty much always going to be the same thing as self, so capture unowned self or weak self instead. I would prefer weak here because self will pretty much always be retained by the class and will only be deallocated when the application terminates.

OtherSVC.upload("Mr. Server, do async stuff plz") { [unowned self] (inImage) in
    self..currentTemp = .cold
    self.aPicture = inImage
    onDone(self.giveMeFive())
}

Upvotes: 3

Related Questions