There are three pieces of code I’d like to revisit briefly, just to make sure you understand them fully.
First, I want to look more closely at how closure capturing works with asyncAfter()
. Here’s some code as an example:
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [unowned self] in
self.doStuff()
}
The call to asyncAfter()
needs two parameters, but we pass the second one in using trailing closure syntax because it’s clearer. The first parameter is specified as a DispatchTime
value, which is the exact time to execute the code. When we specify .now()
, Swift is smart enough to realize that means DispatchTime.now()
, which is the current time. It then lets us add 1 second to it, so that the finished deadline ends up being one second away from now.
Then there’s the [unowned self]
. This is called a capture list, and it gives Swift instructions how to handle values that get used inside the closure. In this case, self.doStuff()
references self
, so the capture list [unowned self]
means “I know you want to capture self
strongly so that it can be used later, but I want you not to have any ownership at all.”
If the closure runs and self
has become nil for some reason, the call to self.doStuff()
will crash: we told Swift not to worry about ownership, then accidentally let self
get destroyed, so the crash is our own fault. As an alternative, we could have written [weak self]
, which would capture self
in the closure as an optional. With that change, you’d need to run this code instead:
self?.doStuff()
Closure capturing can be a complicated topic. In this case, [unowned self]
isn’t really even needed because there’s no chance of a reference cycle: self
doesn’t own DispatchQueue.main
, so the reference will be destroyed once the closure finishes. However, there’s no harm adding [weak self]
, which is why I often include it.
The second piece of code I’d like to review is this, taken from project 15:
UIView.animate(withDuration: 1, delay: 0, options: [], animations: {
switch self.currentAnimation {
case 0:
break
default:
break
}
}) { finished in
sender.isHidden = false
}
At this point in your Swift career you’ve seen several functions that accept closures, but this one takes two: one is a set of animations to perform, and one is a set of actions to run when the animations have completed. I didn’t include closure capturing here because it isn’t needed – the animation closure will be used once then destroyed.
I don’t want to sound like a broken record, but: if you use [weak self]
when it isn’t needed, nothing happens. But if you don’t use it and it was needed, bad things will happen - at the very least you’ll leak memory.
The final piece of code to review is this, also taken from project 15:
self.imageView.transform = CGAffineTransform(rotationAngle: CGFloat.pi)
self.imageView.transform = CGAffineTransform.identity
There are a few things in there that I’d like to recap just briefly:
self
is required when accessing imageView
, because we’re inside a closure and Swift wants us to explicitly acknowledge we recognize self
will be captured.CGFloat.pi
constant is equal to 180 degrees, and it also has equivalents in Float.pi
and Double.pi
– this is just to save you having to typecast values.CGAffineTransform.identity
, has no transformations: it’s not scaled, moved, or rotated, and it’s useful for resetting a transform.TAKE YOUR SKILLS TO THE NEXT LEVEL If you like Hacking with Swift, you'll love Hacking with Swift+ – it's my premium service where you can learn advanced Swift and SwiftUI, functional programming, algorithms, and more. Plus it comes with stacks of benefits, including monthly live streams, downloadable projects, a 20% discount on all books, and free gifts!
Sponsor Hacking with Swift and reach the world's largest Swift community!
Link copied to your pasteboard.