Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I use weak self inside a computed property's closure?

I am confused about using self inside a closure.

When should we declare [weak self] ? An obvious case that I understand is

class Foo{

    var closure: ( Void -> Void )?
    var x = 0

    func doSomething(){
        closure = { [weak self] in
            if let x = self?.x{
                println(x)
            }
        }
    }
}

However, if I want to create a computed property bar, which it has a closure, and it captures self inside. Like this,

extension Foo{

    var bar: Bar{
        let bar = Bar()
        bar.completionHandler = {
            println(self.x)
        }
        return bar
    }
}

Should I use [weak self] inside this closure?

like image 787
nRewik Avatar asked Oct 31 '22 23:10

nRewik


1 Answers

Consider:

extension Foo {
    var bar: Bar {
        let bar = Bar()
        bar.completionHandler = {
            print(self.x)
        }
        return bar
    }
}

Should I use [weak self] inside this closure?

Usually when people ask this question, the real concern is “do I need [weak self] to avoid strong reference cycle?” The answer to that is, no, there is no strong reference cycle here.

If bar were a stored property, the absence of [weak self] would raise red flags. It could easily be problematic if we had a Foo that stored a reference to a Bar which, itself, had a closure with a self reference back to the original Foo. But with this computed property, Foo doesn’t have any strong reference to bar, so the strong reference cycle concerns are largely diminished.

That having been said, I have a hard time imaging where I wouldn’t want to use [weak self]. If bar has a completionHandler, that means it’s likely used in some asynchronous scenario, and the question is whether Foo needs to be retained during this process.

So, the real question of “should” you use [weak self] comes down to what Bar is, and whether it has any reasonably claim of ownership over Foo.

Let’s try to come up with a practical example. (The following is a bit contrived, because I have a hard time imaging a good use-case for this pattern, but bear with me.)

For example, let’s assume that Foo was a Person object and Bar was some URLSessionTask for the image download task:

class Person {
    let name: String
    private(set) var image: UIImage?

    ...
}

extension Person {
    var imageTask: URLSessionTask {
        let url = ...
        return session.dataTask(with: url) { [weak self] data, _, _ in
            guard let data = data, let image = UIImage(data: data) else { return }

            self?.image = image
        }
    }
}

So a controller might say

let person = Person(...)
let task = person.imageTask
task.resume()

In the above example, I happened to use [weak self] in imageTask closure, not because I was worried about any strong reference cycle, but simply because a network task generally has no business claiming a strong reference over the model object. (Then, again, I wouldn’t personally bury network interfaces in model objects, either.) But in this example, one could omit [weak self] reference for this closure if, for example, you wanted to make sure that the Person object was retained until the network request was done (e.g. maybe you wanted to save the results of the network request in some local persistent storage).

All of this having been said, I have a really hard time imaging where I would use the above pattern at all. But the bottom line is that there is no strong reference cycle here, so you theoretically could omit the [weak self] without concern. But in most practical scenarios, you generally would end up using [weak self].

like image 83
Rob Avatar answered Nov 09 '22 17:11

Rob