I know that there are already several questions asked about weak
in the context of animations like Is it necessary to use unowned self in closures of UIView.animateWithDuration(…)? While it is obvious in the latter case, that you can omit weak
, I have still difficulties, to see the reason, why I should not use the weak
pattern in Robs answer about a rotating view. I do not want to disturb the comments there, so I ask the question here.
The code in question is
private func createAnimation() {
animator = UIViewPropertyAnimator.runningPropertyAnimator(withDuration: 4, delay: 0, options: .curveLinear) { [self] in
UIView.animateKeyframes(withDuration: 4, delay: 0) {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 1.0 / 3.0) {
animatedView.transform = .init(rotationAngle: .pi * 2 * 1 / 3)
}
...
}
} completion: { [weak self] _ in
self?.createAnimation()
}
}
Rob used the [weak self]
in the completion
-closure but not in the animations
-closure, where he actually put self
into the capture list to make his intention obvious. How can I know, that UIViewPropertyAnimator.runningPropertyAnimator
will never put the (escaping) animations
-closure into the created animator
-instance-variable?
I don't think, that UIViewPropertyAnimator.runningPropertyAnimator
actually captures the animations
-closure, but as long as I have no idea, how UIViewPropertyAnimator.runningPropertyAnimator
is implemented, or will be implemented in the future, how can I be sure?
Maybe this pseudo-implementation could help to explain, what I mean:
import Foundation
class UIView {
var transform = CGFloat.zero
static func animateKeyFrames(animations: () -> Void) {}
static func addKeyframe(animations: () -> Void) {}
}
class UIViewPropertyAnimator {
var animations: () -> Void = {}
var completion: (() -> Void)? = {}
static func runningPropertyAnimator(animations: @escaping () -> Void,
completion: (() -> Void)?) -> UIViewPropertyAnimator {
let animator = UIViewPropertyAnimator()
animator.animations = animations
animator.completion = completion
return animator
}
}
class ViewController {
var animator: UIViewPropertyAnimator?
let animatedView = UIView()
func viewDidLoad() {
createAnimation()
}
func createAnimation() {
animator = UIViewPropertyAnimator.runningPropertyAnimator(animations: { [weak self] in
UIView.animateKeyFrames(animations: {
UIView.addKeyframe(animations: {
self?.animatedView.transform = .zero
})
})
}, completion: { [weak self] in
self?.animatedView.transform = .zero
})
}
deinit {
print("deinit")
}
}
func createAndRelease() {
let viewController = ViewController()
viewController.viewDidLoad()
}
createAndRelease()
Removing [weak self]
from the animations
or completion
-closure would obviously cause a retain-cycle in my pseudo-code and deinit
would never be called.