ProgrammingiOS Developer

Describe the working mechanism of defer in Swift, and what edge cases and features should be cautious about?

Pass interviews with Hintsage AI assistant

Answer.

defer is a special Swift construct that allows executing a certain block of code right before exiting the scope of a function, regardless of whether we exited it through a regular return or due to an error (throw). Defer is convenient for resource cleanup, undoing changes, or finalizing operations.

Working features:

  • If there are multiple defer statements in a function, they will be executed in reverse order (LIFO — last in, first out structure).
  • defer executes even when an error (throw) occurs or on any return from the function.
  • In a closure, defer is related to the body of the closure, not the point at which the closure is called.

Example:

func testDefer() { print("begin") defer { print("first defer") } defer { print("second defer") } print("end") } // Output: // begin // end // second defer // first defer

Edge cases:

  • defer captures the values of variables at the time the defer is created: if we use variables in a defer closure, changes to them will be visible at the time defer executes.
  • If the function terminates with fatalError — defer will not be called.

Trick question.

Will all defer blocks in a function execute if fatalError is called within it?

Answer: No, if a function calls fatalError (or similar uncontrolled crashes), all deferred blocks will not execute. defer does not guarantee code execution in cases of abnormal application termination.

Example:

func foo() { defer { print("Defer 1") } fatalError("Oops") defer { print("Defer 2") } } foo() // outputs nothing, crash will occur

Examples of real errors due to lack of knowledge of the nuances of the topic.


Story

In a project, defer was used to close a file descriptor. When an error occurred, fatalError was used instead of a proper throw, leading to a leak of the open resource since defer did not run during the crash.


Story

In one function, there were several defer statements, some of which depended on the state of local variables. The developer expected a variable to equal a specific value, but this value changed in defer due to another part of the code, and when defer executed, it used the current value rather than the old value, leading to a transaction rollback bug for the wrong ID.


Story

In a nested closure, a defer block was written, thinking it would execute when exiting the outer function. As a result, this defer executed upon exiting the closure, not the entire function, and the resource was released too early.