0

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....

Yuuu
  • 715
  • 1
  • 9
  • 32
  • General rule of thumb is something that will be executed with a delay should use [weak self]. The animation code without delay doesn’t need to use weak self but the completion call does. Because it triggers after x time. – Lloyd Keijzer Feb 21 '22 at 06:59

1 Answers1

2

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.

Nathan Day
  • 5,981
  • 2
  • 24
  • 40
  • Thank you so much for explaining this in detail. Since I'm a junior developer, I really appreciate your help! I want to make one thing clear that since you told me `they are passed to a function that uses them straight away, then once they are executed they will be deallocated`. Does the "passed to a function" mean passed my block as a parameter or something? (sorry if I misunderstood) I call the code in the post inside of func showFocusView()'s block, so when the showFocusView finishes executing, it will be deallocated? I updated the function in the post to how I used in project. – Yuuu Feb 22 '22 at 05:22
  • yes, so it’s something like a closure you pass to an array for sorting for example, it will use that block only within the scope of the called function, and so it will only have a strong reference to that closure for the life of that function being called, it will then use your sort closure and then return a result, and the memory stack for the called function will not exist any more, and so the,strong reference for you closure is lost, this is different to a closure which an object assigns to an instance variable, and used repeatedly. – Nathan Day Feb 23 '22 at 06:08
  • ↑Thank you for explaining! I've read Lloyd's response to my post and now I'm kinda confused why I need (or I don't need) to put the [weak self] in the animation block and the same for the completion block. So, in each animation block why do I need / or don't need the [weak self]? and why do I need or don't need the [weak self] in the completion block? – Yuuu Feb 24 '22 at 02:07
  • Animations are brief, they execute straight away, data loads are going to to be uncertain amount of time, you will be stuck with the object in the loop until a returns occurs, even if the user does something and its no longer requited, you can always add [weak self] if you are not sure it not much over head. – Nathan Day Feb 24 '22 at 10:00
  • Gotcha. Again, thank you so much for explaining all the details!! – Yuuu Feb 25 '22 at 01:55