Sobes.tech
Junior

What is a closure?

sobes.tech AI

Answer from AI

A closure in Swift is a self-contained block of functionality ({}) that can be passed around and used in your code. Closures can capture and store references to constants and variables from the context in which they are defined.

Examples of usage:

  • As arguments to functions (e.g., Completion Handlers).
  • To define behavior when enumerating collections (e.g., using map, filter, reduce, sorted methods).
  • For deferred execution of code.

Syntax:

// Basic syntax
{ (parameters) -> returnType in
    statements
}

// Example of a closure accepting an Int and returning a String
let integerToString: (Int) -> String = { (number: Int) -> String in
    return "Number: \(number)"
}

Features:

  • Automatic capturing of variables.
  • Ability to define closures without explicitly specifying parameter types and return type (syntactic sugar).
  • Use of shorthand argument names ($0, $1, etc.).
  • Trailing closure syntax.

Variable capturing:

A closure can capture variables from its surrounding environment. By default, capturing occurs by reference. Even if the original variable goes out of scope, the closure still maintains access to it.

func makeIncrementer(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0 // External variable
    let incrementer: () -> Int = { // Closure
        runningTotal += amount // Captures runningTotal and amount
        return runningTotal
    }
    return incrementer
}

let incrementByTen = makeIncrementer(forIncrement: 10)
print(incrementByTen()) // Output: 10
print(incrementByTen()) // Output: 20

Capture Lists:

Used to explicitly specify how variables should be captured — by value or by reference — and to avoid strong reference cycles (retain cycles).

class MyClass {
    var value = 0

    lazy var myClosure: () -> Void = { [self, value] in // Capture self by reference, value by value
        print("Value of self.value: \(self.value)")
        print("Captured value: \(value)")
    }

    deinit {
        print("MyClass instance deallocated")
    }
}

To prevent strong reference cycles when capturing class objects, use [weak self] or [unowned self] syntax.

class ViewController {
    var dataFetcher: DataFetcher?

    func fetchData() {
        dataFetcher?.fetch { [weak self] result in // Weak capture of self
            guard let self = self else { return } // Check for self existence
            // Handle result
        }
    }
}

class DataFetcher {
    var completionHandler: ((Result<String, Error>) -> Void)?

    func fetch(completion: @escaping (Result<String, Error>) -> Void) {
        self.completionHandler = completion
        // Asynchronous operation...
    }
}
What is a closure? — iOS - sobes.tech