I'm learning more about Swift and came across the defer statement recently, which seems really intriguing to me. However I don't really understand it's purpose. Coming from C++ I would have implemented the same functionality using deallocation function and as a matter of fact, as Swift is ARC, it can do the same.
Let's say FooData and BarData both work with data that needs to deallocated.
class FooData {
deinit {
print("FooData being deallocated")
}
}
class BarData {
}
func baz() -> Int {
var a = FooData()
var b = BarData()
defer { print("BarData being deallocated") }
/* sensitive operations that could throw at any time */
return 0
}
baz()
// BarData being deallocated
// FooData being deallocated
So what's the advantage of the defer approach over the deinit approach? Just thinking about using defer for anything besides resource cleanup makes my head hurt...
You are seeing as different but there are not, defer was introduced by Apple as a safe and easy way to handle the clean up before returning, but defer only works for scopes. So let me explain better, if you have some scope defined inside a function an the variable you have created exist only inside the scope you cannot access from the deinit, for example:
func resizeImage(url: NSURL) -> UIImage? {
// ...
let dataSize: Int = ...
let destData = UnsafeMutablePointer<UInt8>.alloc(dataSize)
defer {
destData.dealloc(dataSize)
}
var destBuffer = vImage_Buffer(data: destData, ...)
// scale the image from sourceBuffer to destBuffer
var error = vImageScale_ARGB8888(&sourceBuffer, &destBuffer, ...)
guard error == kvImageNoError
else { return nil }
// create a CGImage from the destBuffer
guard let destCGImage = vImageCreateCGImageFromBuffer(&destBuffer, &format, ...)
else { return nil }
// ...
}
In this case it doesn't make sense define the variable destData as global and we need to deallocate once we finish of work with it, so defer it's the choice.
I think deinit it can be used for more global scope, for example when you implement the Key-Value Observer using NSNotificationCenter or something else you need.
I hope this help you.
Using defer inside a method means that its work will be executed as the method is exiting.
override func viewDidLoad() {
super.viewDidLoad()
print("Step 1")
myFunc()
print("Step 5")
}
func myFunc() {
print("Step 2")
defer { print("Step 3") }
print("Step 4")
}
"Step 1", "Step 2", "Step 4", "Step 3", "Step 5" – steps 3 and 4 are switched because 3 is deferred until the myFunc() method ends, i.e. when it goes out of scope programmatically.
About deinit, this is used to run code before deinitialization. The deinit code is run automatically. Deinitializers are called automatically, just before instance deallocation takes place. You are not allowed to call a deinitializer yourself.
class Item {
init() {
print("init called")
}
deinit {
// Called whenever class stops existing.
print("deinit called")
}
}
// Create optional Item variable.
var i: Item? = Item()
// Set optional to nil to force deinit.
i = nil
In programming some functions always appear in pairs. For example, opening a connection and closing that connection, locking a mutex and unlocking a mutex, incrementing a counter, decrementing a counter, allocating memory, deallocating memory.
The pattern usually looks like this:
lock()
... do something ...
unlock()
The middle part can be complicated and long. There can be returns (e.g. for failed preconditions and Swift recommends this pattern with its guard). Sometimes it's very hard not to forget to include that unlock() in all execution paths.
One way to solve the situation nicely is using a helper function:
func doSomething() {
... do something with returns ...
}
lock()
doSomething()
unlock()
but that's not always possible, e.g. when you have several nested objects.
In C the same pattern was often solved with goto:
x = malloc(...);
y = malloc(...);
if (!precondition) {
goto end;
}
... some code ...
end:
free(y);
free(x);
Modern languages came with a better approach which in Swift is implemented using defer (you can also find defer in Go, for example).
lock()
defer {
unlock()
}
... some code ...
This approach has several benefits:
You can have the calls together, which increases readability and makes it very hard to forget the second call.
All returns, precondition checks, error handling will leave the code in correct state because unlock will be always called correctly. This is similar to finally in exception handling in Java (and other languages)
If you are asking about the difference from deinit, it works in a similar way. However defer can work in functions while deinit works only for classes.
Also note that you could reimplement defer using deinit but the usage would be more complicated and the behavior less predictable.
defer could be called conditionally what is impossible to implement with deinit
var i = 1
func foo()->Int {
if i == 1 {
defer {
i = 0
}
}
return i + 1
}
print("foo:", foo(), "i:", i)
Consider a database transaction. You want to close the connection when you're done, but you want to keep the object around to reinstate connections in the future:
stuct foo {
let db = Database()
func useDatabase() throws {
let connection = db.openConnection()
defer {
// close conenction, but keep db around for future use
connection.close
}
try connection.thisCanThrow()
}
}
This is just one example, but there are many like it. In particular, a lot of cases arise where you want to model the restricted lifespan of a state, without binding it tightly to an object's life time.
C++ heavily relies on RAII. Swift is certainly capable of adhering to the same paradigm, but it can also go beyond it with defer.
Related
With swift compiler optimizations implicitly unwrapped optional variables do not survive the whole scope, but are released immediately after usage.
Here is my environment:
swift --version
outputs
Apple Swift version 5.3.2 (swiftlang-1200.0.45 clang-1200.0.32.28)
Target: x86_64-apple-darwin20.2.0
Xcode version is Version 12.3 (12C33)
Consider this most rudimentary example that shows the issue:
final class SomeClass {
func doSth() {}
deinit {
print("deinit")
}
}
func do() {
var someObject: SomeClass! = SomeClass()
someObject.doSth()
print("done")
}
This should ouput
done
deinit
However, in release builds (with Swift code optimizations enabled "-O") it prints the other way round:
deinit
done
This is ONLY the case for var someObject: SomeClass!.
The following alterations of that code ALL output correctly (meaning the Object is released when the scope of the function is left):
Define var as constant:
func doSthSpecial() {
let someObject: SomeClass! = SomeClass()
someObject.doSth()
print("done")
}
Define var as optional explicitly:
func doSthSpecial() {
var someObject: SomeClass? = SomeClass()
someObject.doSth()
print("done")
}
Access like an optional:
func doSthSpecial() {
var someObject: SomeClass! = SomeClass()
someObject?.doSth()
print("done")
}
These last three implementations all output
done
deinit
in that order.
Somehow this leaves me speechless 🤷♂️.
I understand this optimization, it makes sense. But as a programmer we are used to local variables inside of functions being available until leaving the scope.
The problem I have here is about the lifetime of an object that is stored in such an implicitly unwrapped optional variable. If I have code that depends on the lifetime of this object (which is the case with RxSwift and its DisposeBags for example) then I am getting weird behavior, unexpected behavior!
I could consider this as a bug in Swift, but what do you think? Bug or no bug?
Here is a more real-world scenario with RxSwift where you could be using such a construct:
import UIKit
import RxSwift
final class SomeClass {
func doSth() {}
deinit {
print("deinit")
}
}
class ViewController: UIViewController {
let providePassword = PublishSubject<String>()
lazy var askForPassword: Observable<String> = {
return Observable.create { observer in
_ = self.providePassword.subscribe(observer)
return Disposables.create()
}
.debug(">>> ask for password signal")
}()
private func performAsyncSyncTask() {
DispatchQueue.global().async {
var disposeBag: DisposeBag! = DisposeBag()
let sema = DispatchSemaphore(value: 0)
self.askForPassword
.subscribe(onNext: { pw in
print(pw)
sema.signal()
})
.disposed(by: disposeBag)
_ = sema.wait(timeout: DispatchTime.distantFuture)
disposeBag = nil
}
}
#IBAction func startAskPassword(sender: AnyObject) {
self.performAsyncSyncTask()
}
#IBAction func sendPassword(sender: AnyObject) {
self.providePassword.on(.next("hardcoded pw"))
}
}
The problem here is: When executing self.performAsyncSyncTask() it is subscribed to askForPassword but because in optimized builds the implicitly unwrapped optional variable is purged immediately after using it in .disposed(by: disposeBag).
This destroys the signal immediately after subscribing to it.
But as a programmer we are used to local variables inside of functions being available until leaving the scope.
This hasn't been the case since ARC was first released for ObjC. ARC has always had the option to release objects after their last use (and very often makes use of this). This is by design, and is not a bug in Swift (or in ObjC, where it's also true).
In Swift, if you want to extend the lifetime of an object beyond its last use,withExtendedLifetime is explicitly for this purpose.
var someObject: SomeClass! = SomeClass()
withExtendedLifetime(someObject) {
someObject.doSth()
print("done")
}
Keep in mind that it is legal for objects to have balanced retain/autorelease calls on them, which may cause them to outlive their scope as well. This is much less common in Swift, but still legal and happens if you pass a Swift object to ObjC (which can happen in many places you may not expect).
You should be very careful relying on when deinit will be called. It can surprise you, and isn't even promised in all cases (for example, deinit is not called during program quit on Mac, which tends to surprise C++ developers).
IMO performAsyncSyncTask is a dangerous pattern, and should be redesigned with clearer ownership. I don't do enough RxSwift work to immediately redesign it, but blocking the whole thread on a DispatchSemaphore seems the wrong way to integrate with any reactive system. Threads are a finite resource, and this forces the system to create more while this one is blocked doing nothing.
Note: This question is basically the same as this one, but for Swift 4 or 5.
Say I have a class that captures a closure:
class CallbackHolder {
typealias Callback = (String) -> Void
var callback: Callback
init(_ callback: #escaping Callback) {
self.callback = callback
}
func useCallback() {
self.callback("Hi!")
}
}
This class simply holds a callback in a variable, and has a function that uses that callback.
Now, say I have a client class that owns such a callback holder. This class wants the callback holder to call one of its methods as the callback:
class Client {
func callback(string: String) {
print(string)
}
lazy var callbackOwner = CallbackOwner(callback: callback)
deinit {
print("deinit")
}
}
This class has a callback, a callback owner that calls that callback, and a deinit that prints something so that we know whether we have a retain cycle (no deinit = retain cycle).
We can test our setup with the following test function:
func test() {
Client().callbackOwner.useCallback()
}
We want the test function to print both Hi! and deinit, so that we know that the callback works, and that the client does not suffer from a retain cycle.
The above Client implementation does in fact have a retain cycle -- passing the callback method to the callback owner causes the owner to retain the client strongly, causing a cycle.
Of course, we can fix the cycle by replacing
lazy var callbackOwner = CallbackOwner(callback: callback)
with
lazy var callbackOwner = CallbackOwner(callback: { [weak self] in
self?.callback($0)
})
This works, but:
it is tedious (compare the amount of code we now need)
it is dangerous (every new client of my CallbackOwner class must remember to do it this way, even though the original way is completely valid syntax, otherwise they will get a retain cycle)
So I am looking for a better way. I would like to capture the callback weakly at the CallbackOwner, not at the client. That way, new clients don't have to be aware of the danger of the retain cycle, and they can use my CallbackOwner in the most intuitive way possible.
So I tried to change CallbackOwner's callback property to
weak var callback: Callback?
but of course, Swift only allows us to capture class types weakly, not closures or methods.
At the time when this answer was written, there did not seem to be a way to do achieve what I'm looking for, but that was over 4 years ago. Has there been any developments in recent Swift versions that would allow me to pass a method to a closure-capturing object without causing a retain cycle?
Well one obvious way to do this would be to not hold a reference to CallbackHolder in Client i.e.
class Client {
func callback(string: String) {
print(string)
}
var callbackOwner: CallbackHolder { return CallbackHolder(callback) }
deinit {
print("deinit")
}
}
In the above case, I'd probably make CallbackHolder a struct rather than a class.
Personally, I don't see any value in wrapping the callback in a class at all. Just pass the callback around, or even Client. Maybe make a protocol for it to adhere to
protocol Callbackable
{
func callback(string: String)
}
extension Callbackable
{
func useCallback() {
self.callback(string: "Hi!")
}
}
class Client: Callbackable {
func callback(string: String) {
print(string)
}
deinit {
print("deinit")
}
}
func test(thing: Callbackable) {
thing.useCallback()
}
test(thing: Client())
I feel that I've always misunderstood that when reference cycles are created. Before I use to think that almost any where that you have a block and the compiler is forcing you to write .self then it's a sign that I'm creating a reference cycle and I need to use [weak self] in.
But the following setup doesn't create a reference cycle.
import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution
class UsingQueue {
var property : Int = 5
var queue : DispatchQueue? = DispatchQueue(label: "myQueue")
func enqueue3() {
print("enqueued")
queue?.asyncAfter(deadline: .now() + 3) {
print(self.property)
}
}
deinit {
print("UsingQueue deinited")
}
}
var u : UsingQueue? = UsingQueue()
u?.enqueue3()
u = nil
The block only retains self for 3 seconds. Then releases it. If I use async instead of asyncAfter then it's almost immediate.
From what I understand the setup here is:
self ---> queue
self <--- block
The queue is merely a shell/wrapper for the block. Which is why even if I nil the queue, the block will continue its execution. They’re independent.
So is there any setup that only uses queues and creates reference cycles?
From what I understand [weak self] is only to be used for reasons other than reference cycles ie to control the flow of the block. e.g.
Do you want to retain the object and run your block and then release it? A real scenario would be to finish this transaction even though the view has been removed from the screen...
Or you want to use [weak self] in so that you can exit early if your object has been deallocated. e.g. some purely UI like stopping a loading spinner is no longer needed
FWIW I understand that if I use a closure then things are different ie if I do:
import PlaygroundSupport
import Foundation
PlaygroundPage.current.needsIndefiniteExecution
class UsingClosure {
var property : Int = 5
var closure : (() -> Void)?
func closing() {
closure = {
print(self.property)
}
}
func execute() {
closure!()
}
func release() {
closure = nil
}
deinit {
print("UsingClosure deinited")
}
}
var cc : UsingClosure? = UsingClosure()
cc?.closing()
cc?.execute()
cc?.release() // Either this needs to be called or I need to use [weak self] for the closure otherwise there is a reference cycle
cc = nil
In the closure example the setup is more like:
self ----> block
self <--- block
Hence it's a reference cycle and doesn't deallocate unless I set block to capturing to nil.
EDIT:
class C {
var item: DispatchWorkItem!
var name: String = "Alpha"
func assignItem() {
item = DispatchWorkItem { // Oops!
print(self.name)
}
}
func execute() {
DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: item)
}
deinit {
print("deinit hit!")
}
}
With the following code, I was able to create a leak ie in Xcode's memory graph I see a cycle, not a straight line. I get the purple indicators. I think this setup is very much like how a stored closure creates leaks. And this is different from your two examples, where execution is never finished. In this example execution is finished, but because of the references it remains in memory.
I think the reference is something like this:
┌─────────┐─────────────self.item──────────────▶┌────────┐
│ self │ │workItem│
└─────────┘◀︎────item = DispatchWorkItem {...}───└────────┘
You say:
From what I understand the setup here is:
self ---> queue
self <--- block
The queue is merely a shell/wrapper for the block. Which is why even if I nil the queue, the block will continue its execution. They’re independent.
The fact that self happens to have a strong reference to the queue is inconsequential. A better way of thinking about it is that a GCD, itself, keeps a reference to all dispatch queues on which there is anything queued. (It’s analogous to a custom URLSession instance that won’t be deallocated until all tasks on that session are done.)
So, GCD keeps reference to the queue with dispatched tasks. The queue keeps a strong reference to the dispatched blocks/items. The queued block keeps a strong reference to any reference types they capture. When the dispatched task finishes, it resolves any strong references to any captured reference types and is removed from the queue (unless you keep your own reference to it elsewhere.), generally thereby resolving any strong reference cycles.
Setting that aside, where the absence of [weak self] can get you into trouble is where GCD keeps a reference to the block for some reason, such as dispatch sources. The classic example is the repeating timer:
class Ticker {
private var timer: DispatchSourceTimer?
func startTicker() {
let queue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".ticker")
timer = DispatchSource.makeTimerSource(queue: queue)
timer!.schedule(deadline: .now(), repeating: 1)
timer!.setEventHandler { // whoops; missing `[weak self]`
self.tick()
}
timer!.resume()
}
func tick() { ... }
}
Even if the view controller in which I started the above timer is dismissed, GCD keeps firing this timer and Ticker won’t be released. As the “Debug Memory Graph” feature shows, the block, created in the startTicker routine, is keeping a persistent strong reference to the Ticker object:
This is obviously resolved if I use [weak self] in that block used as the event handler for the timer scheduled on that dispatch queue.
Other scenarios include a slow (or indefinite length) dispatched task, where you want to cancel it (e.g., in the deinit):
class Calculator {
private var item: DispatchWorkItem!
deinit {
item?.cancel()
item = nil
}
func startCalculation() {
let queue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".calcs")
item = DispatchWorkItem { // whoops; missing `[weak self]`
while true {
if self.item?.isCancelled ?? true { break }
self.calculateNextDataPoint()
}
self.item = nil
}
queue.async(execute: item)
}
func calculateNextDataPoint() {
// some intense calculation here
}
}
All of that having been said, in the vast majority of GCD use-cases, the choice of [weak self] is not one of strong reference cycles, but rather merely whether we mind if strong reference to self persists until the task is done or not.
If we’re just going to update the the UI when the task is done, there’s no need to keep the view controller and its views in the hierarchy waiting some UI update if the view controller has been dismissed.
If we need to update the data store when the task is done, then we definitely don’t want to use [weak self] if we want to make sure that update happens.
Frequently, the dispatched tasks aren’t consequential enough to worry about the lifespan of self. For example, you might have a URLSession completion handler dispatch UI update back to the main queue when the request is done. Sure, we theoretically would want [weak self] (as there’s no reason to keep the view hierarchy around for a view controller that’s been dismissed), but then again that adds noise to our code, often with little material benefit.
Unrelated, but playgrounds are a horrible place to test memory behavior because they have their own idiosyncrasies. It’s much better to do it in an actual app. Plus, in an actual app, you then have the “Debug Memory Graph” feature where you can see the actual strong references. See https://stackoverflow.com/a/30993476/1271826.
Since the Xcode 10.2 (Swift 5) the defer statement at the end of the deinit scope produces:
'defer' statement before end of scope always executes immediately; replace with 'do' statement to silence this warning
Let's take a look at this example:
var foo: String {
didSet {
// smt
}
}
deinit {
defer { <--- Warning
foo = bar
}
}
Of course it's possible to get rid of this warning by moving the code from the observer to a method and call it explicitly but…
What's the point of this warning? - Isn't it reasonable to have the defer statement in the deinit? (e.g. to be able to trigger properties' observers).
The warning is correct in that the use of defer here doesn't change the order of execution of your program, which is what the statement is designed for. It is however unfortunate that the suggested replacement otherwise changes the behaviour of your program (filed a bug: SR-10207).
It's worth noting that the use of defer to trigger a property observer is a bit of a hack that only works because the type checker considers it to be a different context to the deinit body. You can also achieve the same result with a closure expression:
deinit {
{ foo = bar }()
}
Ideally, there would be some form of syntax that lets you tell Swift "don't perform a direct-to-storage access here", so that such workarounds aren't necessary, but there isn't currently.
A less hacky workaround is to pull out the desired logic of the deinitialiser into a separate method, which puts the logic in a context where property accesses are done normally:
class C {
var bar = ""
var foo: String {
didSet {
// smt
}
}
init(foo: String) { self.foo = foo }
private func doDeinit() {
foo = bar
}
deinit {
doDeinit()
}
}
The answers I've seen so far (1, 2, 3) recommend using GCD's dispatch_once thus:
var token: dispatch_once_t = 0
func test() {
dispatch_once(&token) {
print("This is printed only on the first call to test()")
}
print("This is printed for each call to test()")
}
test()
Output:
This is printed only on the first call to test()
This is printed for each call to test()
But wait a minute. token is a variable, so I could easily do this:
var token: dispatch_once_t = 0
func test() {
dispatch_once(&token) {
print("This is printed only on the first call to test()")
}
print("This is printed for each call to test()")
}
test()
token = 0
test()
Output:
This is printed only on the first call to test()
This is printed for each call to test()
This is printed only on the first call to test()
This is printed for each call to test()
So dispatch_once is of no use if we I can change the value of token! And turning token into a constant is not straightforward as it needs to of type UnsafeMutablePointer<dispatch_once_t>.
So should we give up on dispatch_once in Swift? Is there a safer way to execute code just once?
A man went to the doctor, and said "Doctor, it hurts when I stamp on my foot". The doctor replied, "So stop doing it".
If you deliberately alter your dispatch token, then yes - you'll be able to execute the code twice. But if you work around the logic designed to prevent multiple execution in any way, you'll be able to do it. dispatch_once is still the best method to ensure code is only executed once, as it handles all the (very) complex corner cases around initialisation and race conditions that a simple boolean won't cover.
If you're worried that someone might accidentally reset the token, you can wrap it up in a method and make it as obvious as it can be what the consequences are. Something like the following will scope the token to the method, and prevent anyone from changing it without serious effort:
func willRunOnce() -> () {
struct TokenContainer {
static var token : dispatch_once_t = 0
}
dispatch_once(&TokenContainer.token) {
print("This is printed only on the first call")
}
}
Static properties initialized by a closure are run lazily and at most once, so this prints only once, in spite of being called twice:
/*
run like:
swift once.swift
swift once.swift run
to see both cases
*/
class Once {
static let run: Void = {
print("Behold! \(__FUNCTION__) runs!")
return ()
}()
}
if Process.arguments.indexOf("run") != nil {
let _ = Once.run
let _ = Once.run
print("Called twice, but only printed \"Behold\" once, as desired.")
} else {
print("Note how it's run lazily, so you won't see the \"Behold\" text now.")
}
Example runs:
~/W/WhenDoesStaticDefaultRun> swift once.swift
Note how it's run lazily, so you won't see the "Behold" text now.
~/W/WhenDoesStaticDefaultRun> swift once.swift run
Behold! Once runs!
Called twice, but only printed "Behold" once, as desired.
I think the best approach is to just construct resources lazily as needed. Swift makes this easy.
There are several options. As already mentioned, you can initialize a static property within a type using a closure.
However, the simplest option is to define a global variable (or constant) and initialize it with a closure then reference that variable anywhere the initialization code is required to have happened once:
let resourceInit : Void = {
print("doing once...")
// do something once
}()
Another option is to wrap the type within a function so it reads better when calling. For example:
func doOnce() {
struct Resource {
static var resourceInit : Void = {
print("doing something once...")
}()
}
let _ = Resource.resourceInit
}
You can do variations on this as needed. For example, instead of using the type internal to the function, you can use a private global and internal or public function as needed.
However, I think the best approach is just to determine what resources you need to initialize and create them lazily as global or static properties.
For anyone who stumbles on this thread... We ran into a similar situation at Thumbtack and came up with this: https://www.github.com/thumbtack/Swift-RunOnce. Essentially, it lets you write the following
func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated: Bool)
runOnce {
// One-time code
}
}
I also wrote a blog post explaining how the code works, and explaining why we felt it was worth adding to our codebase.
I found this while searching for something similar: Run code once per app install. The above solutions only work within each app run. If you want to run something once across app launches, do this:
func runOnce() {
if UserDefaults.standard.object(forKey: "run_once_key") == nil {
UserDefaults.standard.set(true, forKey: "run_once_key")
/* run once code */
} else {
/* already ran one time */
}
}
If the app is deleted and re-installed, this will reset.
Use NSUbiquitousKeyValueStore for tracking a value across installs and devices as long as user using same appleID.