Skip to main content
  1. Posts/

Swift: defer

·724 words·4 mins

The keyword defer in Swift is not often encountered. Especially when you start developing. Nevertheless it has a certain usefulness and can be very useful.

Definition #

defer is a Swift keyword used to define code to execute before leaving the current scope:

func changeTitle(_ title: String) {
  defer {
    print("After")
  }
  print("Before")
  myTitleLabel.text = title
}

At first sight and if we only consider the order in which the instructions are written, we could believe that we will have the following output:

After
Before

But in reality and as previously mentioned, everything contained in the defer block will be executed afterwards, so we will have the following result:

Before
After

Execution order #

It is important to note that it is possible to define several defer blocks in a single method:

func changeTitle(_ title: String) {
  defer {
    print("After")
  }
  defer {
    print("After after or After before?")
  }
  print("Before")
  myTitleLabel.text = title
}

But in what order will the different blocks be executed?

It’s simple but not very intuitive: in the reverse order of writing. In this case we will have the following output:

Before
After after or After before?
After

So fine, but why do it? Let’s continue together to better understand the importance of this keyword.

Try … catch … finally #

If like me you have been working with C# for a long time, you have probably already used the try...catch...finally syntax. And it’s especially this last one finally that we are interested in!

In C#, finally allows to execute one or more statements, no matter if the previous code was executed correctly or if an error was encountered. Very useful ! Especially when you have to manage semaphores or disposables.

But in Swift this keyword does not exist. So how to do it? You see what I mean, it’s thanks to the defer keyword.

Let’s take the case of a WebSocket communication. We want to send a message and whether it works or not, we want to close the WebSocket bridge. If we don’t use the defer keyword it would look like this:

final class WebSocketWrapper {
  
  func sendMessage(_ message: String) {
    openBridge()
    do {
      try sendMessageOnBridge(message)
      closeBridge()
    } catch {
      closeBridge()
    }
  }
  
  private func sendMessageOnBridge(_ msg: String) throws {
    // We send message
  }
  
  private func openBridge() {
    // We open bridge here
  }
  
  private func closeBridge() {
    // We close bridge here
  }
    
}

Note that the closeBridge method is called twice. Once if everything goes well and once in the catch in case it didn’t go well. And you know as well as I do, duplicating code is never a good idea.

Fortunately with defer it is possible to avoid this:

final class WebSocketWrapper {
  
  func sendMessage(_ message: String) {
    defer {
      closeBridge()
    }
    openBridge()
    do {
      try sendMessageOnBridge(message)
    } catch {
      // Log
    }
  }
  
  private func sendMessageOnBridge(_ msg: String) throws {
    // We send message
  }
  
  private func openBridge() {
    // We open bridge here
  }
  
  private func closeBridge() {
    // We close bridge here
  }
}

It is now much cleaner!

Memory management #

Of course it is becoming increasingly rare to manage memory with high level languages like Swift, but from time to time it is still the case.

If like me you use UnsafeMutablePointer you know that it is necessary to deallocate the memory after its use and this in case of success or failure of the method :

do {

  let pointer = UnsafeMutableRawPointer.allocate(
    byteCount: byteCount,
    alignment: alignment)

  defer {
    pointer.deallocate()
  }
  
  pointer.storeBytes(of: 42, as: Int.self)
  pointer.advanced(by: stride).storeBytes(of: 6, as: Int.self)
  pointer.load(as: Int.self)
  pointer.advanced(by: stride).load(as: Int.self)
  
  let bufferPointer = UnsafeRawBufferPointer(start: pointer, count: byteCount)
  for (index, byte) in bufferPointer.enumerated() {
    print("byte \(index): \(byte)")
  }
}

Here, no matter what happens, I know with certainty that my pointer will be deallocated and will not create a memory leak.

Loops #

It is also possible to use defer in a loop statement, for example a for :

for i in values {
    defer { print ("Defer \(i)") }
    print ("Start \(i)")
    print ("End \(i)")
}

In this case the defer will be executed as many times as there are turns in the loop.

Conclusion #

And you, in your project, do you need to add defer to simplify or secure your code? I hope this article has been useful and that you have learned something new about Swift.