Search code examples
swiftescapingguardweak

Use [weak self] in the nested block in swift


I'm working on calling an animation in Swift and I'm a bit confused with using [weak self] in the nested block. I saw some other posts related to this question, but it confused me because some say they need the weak self, and some don't.

My animation block is something like this.

func showFocusView() {
    UIView.animate(withDuration: 0.3, delay: 0, options: [.curveEaseInOut]) { [weak self] in
        guard let strongSelf = self else { return }
        strongSelf.focusView.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
        strongSelf.focusView.alpha = 1
    } completion: { animated in
        if !animated {
            return
        } else {
           UIView.animate(withDuration: 0.1, delay: 0, options: [.curveEaseInOut]) { [weak self] in
                guard let strongSelf = self else { return }
                strongSelf.focusView.transform = .identity
           } completion: { animated in
                if !animated {
                    return
                } else {
                    UIView.animate(withDuration: 1.5, delay: 0, options: [.curveEaseInOut]) { [weak self] in
                        guard let strongSelf = self else { return }
                        strongSelf.focusView.alpha = 0.5
                    } completion: { animated in
                        if !animated {
                            return
                        } else {
                            UIView.animate(withDuration: 0.5, delay: 0, options: [.curveEaseInOut]) { [weak self] in
                                guard let strongSelf = self else { return }
                                strongSelf.focusView.alpha = 0
                            }
                        }
                    } 
                }
            }
        }
    }
}

My assumption is that sing the animate method's animations block allows escaping,

class func animate(withDuration duration: TimeInterval, 
    animations: @escaping () -> Void, 
    completion: ((Bool) -> Void)? = nil)

I made each animation block a weak self, and when the animated block is called, it is not sure if the self still exists or not. So, I created a strongSelf variable using a guard statement. I did that for every block to make sure the existence of the self, but should I include them in every block, or I shouldn't? I'd like to know why I need / don't need the block....


Solution

  • If you don't use [weak self], then for the life time of that block, you can have a circular reference between it and the object self refers to, when an object loses a reference to it, its reference drops by one, when it reaches zero then it is deallocates and reduces the reference count of any object it has a reference to. If you have a circular reference then neither are going to reach zero, because neither is going to get to zero to deallocate itself and reduce the reference to the other. For regular objects this is a problem because they will never be deallocated, for block though, it can depend on how they are used, if they are passed to a function that uses them straight away, then once they are executed they will be deallocated, and any circular references will be cut, it may be even beneficial that whilst your block is executing that it and self can't go away, but if the block is retained as an instance variable to be called, then you have a circular reference that will never go away. The way to deal with is is to use [weak self], saying references to self are weak in this block, you can then just deal with that on each time you use it, self?.myFunction() for example, or you can create a strong reference at the beginning, it used to be you used to have to use a different variable to self, but now you can go

    guard let self = self else { return }
    

    one of the good things of doing it this way, you are saying if it gets this far I want the block to execute completely, you have created a circular reference for the execution of the block and it will not go away until the block finishes executing. For example if you have a few functions starting with self?, then mid way through your block executing self could become nil, and only the first few functions are executed and the later are not.