+1

Swift Closures

Introduction

Closures in Swift are similar to blocks in C and Objective-C and lambdas in other programming languages.

Definition : Closures are self-contained blocks of functionality that can be passed around and used in your code.

Closures can capture and store references to any constants and variables from the context in which they are defined. This is known as closing over those constants and variables. Swift handles all of the memory management of capturing for you. Swift’s closure expressions have a clean, clear style, with optimizations that encourage brief, clutter-free syntax in common scenarios. These optimizations include:

  • Defining a closure
  • Shorthand argument names
  • Capturing Values and Reference Types

Defining a closure

Closures are typically enclosed in curly braces { } and are defined by a function type () -> (), where -> separates the arguments and the return type, followed by the in keyword which separates the closure header from its body.


{ (params) -> returnType in
  statements
}

An example could be the map function applied to an Array:


let listOfName = [“dina”, “prema”, “belash”]
	listOfName.map({
  	(listOfName: String) -> String in "\(listOfName) has been trained!"
})

Closure expression syntax can use constant parameters, variable parameters, and inout parameters. Default values cannot be provided. Variadic parameters can be used if you name the variadic parameter. Tuples can also be used as parameter types and return types.

The example below shows a closure expression version of the backwards (:😃 function from earlier:


reversed = names.sort({ (firstString:  a href="" String /a , secondString:  a href="" String /a ) ->  a href="" Bool /a  in
    return firstString > secondString
})

the declaration of parameters and return type for this inline closure is identical to the declaration from the backwards(:😃 function. In both cases, it is written as


(firstString: String, secondString: String) -> Bool.

However, for the inline closure expression, the parameters and return type are written inside the curly braces, not outside of them. The start of the closure’s body is introduced by the in keyword. This keyword indicates that the definition of the closure’s parameters and return type has finished, and the body of the closure is about to begin.

Because the body of the closure is so short, it can even be written on a single line:


reversed = names.sort( { (firstString:  a href="" String /a , secondString:  a href="" String /a ) ->  a href="" Bool /a  in return firstString > secondString } )

This illustrates that the overall call to the sort(_😃 method has remained the same. A pair of parentheses still wrap the entire argument for the method. However, that argument is now an inline closure.

Shorthand Argument Names

Closure arguments can be references by position ($0, $1, ...) rather than by name. Swift automatically provides shorthand argument names to inline closures, which can be used to refer to the values of the closure’s arguments by the names $0, $1, $2, and so on.


applyMutliplicationOperation(2, {$0 * 20})

Furthermore, when a closure is the last argument of a function, parenthesis can be omitted as such:


applyMutliplicationOperation(2) {$0 * 20}

Capturing Values and Reference Types

Capturing constants and variables values is done with the help of closures. It further refers and modify the values for those constants and variables inside the closure body even though the variables no longer exists. Capturing constant and variable values is achieved by using nested function by writing function with in the body of other function. A nested function captures −

  • Outer function arguments.
  • Capture constants and variables defined within the Outer function.

When a constant or a variable is declared inside a function, reference to that variables are also automatically created by the closure. It also provides the facility to refer more than two variables as the same closure as follows −


let decrem = calcDecrement(forDecrement: 18)
decrem()

Here oneDecrement and Decrement variables will both point the same memory block as closure reference.

func calcDecrement(forDecrement total: Int) -> () -> Int {
   var overallDecrement = 100
   func decrementer() -> Int {
      overallDecrement -= total
      println(overallDecrement)
      return overallDecrement
   }
   return decrementer
}
let decrem = calcDecrement(forDecrement: 18)
decrem()
decrem()
decrem()

When we run the above program using playground, we get the following result −

82
64
46

When each and every time the outer function calcDecrement is called it invokes the decrementer() function and decrements the value by 18 and returns the result with the help of outer function calcDecrement. Here calcDecrement acts as a closure. Even though the function decrementer() does not have any arguments closure by default refers to variables 'overallDecrement' and 'total' by capturing its existing values. The copy of the values for the specified variables are stored with the new decrementer() function. Swift handles memory management functions by allocating and deallocating memory spaces when the variables are not in use.

Conclusion

Now you should know the basic syntax of closures in Swift. For some more advanced level stuff on closures, check out the Swift Programming Language on iBooks.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí