Search code examples
swiftclosuresblockselfweak

In Swift, how would I reference `self` in nested timers?


If I would nest two timers that are both properties of the class they are referenced in, would I have to call [weak self] in both or just the first timer block?

timer1 = Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { [weak self] _ in
    print(self)
    self?.timer2 = Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { [weak self] _ in
        print(self)
    }
}

Solution

  • As long as you don't convert the weak reference into a strong one outside of your nested closure, the weak reference is passed into the nested closure, so you don't need to make it weak again.

    So the below code is just fine:

    timer1 = Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { [weak self] _ in
        print(self)
        self?.timer2 = Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { _ in
            print(self)
        }
    }
    

    You can confirm this by the fact that you'll get the warning

    Expression implicitly coerced from 'Timers?' to 'Any'

    On both print statements.

    You always need to create a weak reference in the outer-most closure where you reference self, otherwise your outer closure will create a strong-reference cycle, so whether the inner one creates a weak reference or not won't make a difference.

    This creates a strong reference cycle, since to access timer2, you use a strong reference to self.

    timer1 = Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { _ in
        print(self)
        self.timer2 = Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { [weak self] _ in
            print(self)
        }
    }
    

    As long as you convert the weak reference to a strong one using a variable with a different name, the reference to self will be weak and hence you don't need to make it weak again.

    So the below code is also free of strong reference cycles.

    timer1 = Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { [weak self] _ in
        guard let strongSelf = self else { return }
        print(strongSelf)
        strongSelf.timer2 = Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { _ in
            guard let self = self else { return }
            print(self)
        }
    }