Swift Tutorial
Swift Data Types
Swift Control Flow
Swift Functions
Swift Collections
Swift OOPs
Swift Additional Topics
In Swift, when we talk about closures, particularly in the context of function parameters, we often come across the terms "escaping" and "non-escaping." These terms describe the lifecycle of the closure relative to the function it's passed to.
Example:
func performOperation(_ closure: () -> Void) { print("Start of function") closure() print("End of function") } performOperation { print("Closure is executed") }
Output:
Start of function Closure is executed End of function
@escaping
attribute before the closure parameter��s type.[weak self]
or [unowned self]
.Example:
var storedClosure: (() -> Void)? func storeClosureForLater(_ closure: @escaping () -> Void) { storedClosure = closure } storeClosureForLater { print("Stored closure is now executed") } storedClosure?()
Output:
Stored closure is now executed
Memory Management and Retain Cycles: Escaping closures can lead to retain cycles if you're not careful, especially when referencing self
or other objects within the closure. Using capture lists ([weak self]
or [unowned self]
) can help prevent these retain cycles.
Performance: Non-escaping closures allow the compiler to make certain optimizations, knowing that the closure's scope is limited to the lifecycle of the function.
Immutable Variables: In non-escaping closures, you can freely mutate variables from the surrounding scope. In escaping closures, those variables need to be declared with var
to be mutable.
In summary, the distinction between escaping and non-escaping closures is essential in Swift to manage memory efficiently, ensure performant code, and maintain a clear understanding of a closure's lifecycle and its interactions with other parts of the code.
Swift escaping closures vs non-escaping closures:
func performOperation(completion: @escaping () -> Void) { DispatchQueue.main.async { completion() } } // Non-escaping closure func simpleOperation(completion: () -> Void) { // ... completion() }
When to use escaping closures in Swift:
func fetchData(completion: @escaping (Result<Data, Error>) -> Void) { // Asynchronous data fetching }
How to declare escaping closures in Swift:
@escaping
in the parameter list.func performOperation(completion: @escaping () -> Void) { // ... completion() }
Non-escaping closures in Swift examples:
func simpleOperation(completion: () -> Void) { // ... completion() }
Swift @escaping attribute explained:
@escaping
attribute is used to indicate that a closure parameter outlives the function it's passed to.func performOperation(completion: @escaping () -> Void) { // ... completion() }
Escaping closures in asynchronous Swift functions:
func fetchData(completion: @escaping (Result<Data, Error>) -> Void) { // Asynchronous data fetching }
Benefits of non-escaping closures in Swift:
func simpleOperation(completion: () -> Void) { // ... completion() }
Handling retain cycles with escaping closures in Swift:
self
. Use weak
or unowned
references to avoid strong reference cycles.class MyClass { var closure: (() -> Void)? func setupClosure() { closure = { [weak self] in self?.doSomething() } } func doSomething() { // Implementation } }
Autoclosures and escaping in Swift:
func performOperation(completion: @escaping @autoclosure () -> Void) { DispatchQueue.main.async { completion() } }
Escaping closures in completion handlers:
func fetchData(completion: @escaping (Result<Data, Error>) -> Void) { // Asynchronous data fetching }
Closure capturing list in Swift:
class MyClass { var closure: (() -> Void)? func setupClosure() { closure = { [weak self, unowned someObject] in guard let strongSelf = self else { return } strongSelf.doSomething() } } func doSomething() { // Implementation } }
Swift escaping closures in APIs:
func performOperation(completion: @escaping () -> Void) { // ... completion() }