YouTip LogoYouTip

Swift Closures

1. Inferring parameter and return value types from context\n2. Implicit return from single-expression closures (i.e., if the closure body has only one line of code, `return` can be omitted)\n3. Using shorthand parameter names like `$0`, `$1` (starting from 0, representing the i-th parameter...)\n4. Providing trailing closure syntax\n\n### Syntax\n\nThe following defines a closure syntax that takes parameters and returns a specified type:\n\n{(parameters) -> return type in statements }\n### Instance\n\nimport Cocoalet studname = { print("Swift closure instance.") } studname()\nThe output of the above program execution is:\n\nSwift closure instance.\nThe following closure form takes two parameters and returns a Boolean value:\n\n{(Int, Int) -> Bool in Statement1 Statement 2 --- Statement n }\n### Instance\n\nimport Cocoalet divide = {(val1: Int, val2: Int) -> Int in return val1 / val2 }let result = divide(200, 20)print (result)\nThe output of the above program execution is:\n\n10\n\n* * *\n\n## Closure Expressions\n\nClosure expressions are a way to build inline closures using concise syntax. Closure expressions provide several syntax optimizations, making writing closures simple and clear.\n\n* * *\n\n### The sorted Method\n\nThe Swift standard library provides a method called **sorted(by:)**, which sorts the values in an array of a known type based on the sorting closure function you provide.\n\nAfter sorting is complete, the `sorted(by:)` method returns a new array of the same size as the original array, containing elements of the same type that are correctly sorted. The original array is not modified by the `sorted(by:)` method.\n\nThe `sorted(by:)` method requires two parameters to be passed in:\n\n * An array of a known type\n * A closure function, which needs to take two values of the same type as the array elements and return a Boolean value to indicate whether the first parameter passed in should come before or after the second parameter when the sorting is complete. If the first parameter value appears before the second parameter value, the sorting closure function needs to return **true**, and vice versa, return **false**.\n\n### Instance\n\nimport Cocoalet names = ["AT", "AE", "D", "S", "BE"]// use a normal function(or embedded function)provide a sorting function,The closure function type must be(String, String) -> Bool。 func backwards(s1: String, s2: String) -> Bool { return s1 > s2 }var reversed = names.sorted(by: backwards)print(reversed)\nThe output of the above program execution is:\n\n["S", "D", "BE", "AT", "AE"]\nIf the first string (s1) is greater than the second string (s2), the `backwards` function returns `true`, indicating that s1 should appear before s2 in the new array. For characters in a string, "greater than" means "appears later in alphabetical order". This means the letter "B" is greater than the letter "A", and the string "S" is greater than the string "D". It will perform a reverse alphabetical sort, so "AT" will be ranked before "AE".\n\n* * *\n\n## Shorthand Parameter Names\n\nSwift automatically provides shorthand parameter names for inline functions, and you can directly call the closure's parameters in order using `$0`, `$1`, `$2`.\n\n### Instance\n\nimport Cocoalet names = ["AT", "AE", "D", "S", "BE"]var reversed = names.sorted( by: { $0 > $1 } )print(reversed)\n`$0` and `$1` represent the first and second `String` type parameters in the closure.\n\nThe output of the above program execution is:\n\n["S", "D", "BE", "AT", "AE"]\nIf you use shorthand parameter names in a closure expression, you can omit their definitions in the closure parameter list, and the types corresponding to the shorthand parameter names will be inferred through the function type. The `in` keyword can also be omitted.\n\n* * *\n\n## Operator Functions\n\nActually, there is an even shorter way to write the closure expression in the above example.\n\nSwift's `String` type defines a string implementation for the greater-than sign (`>`), which acts as a function that takes two `String` type parameters and returns a `Bool` type value. This exactly matches the function type required for the second parameter of the `sort(_:)` method. Therefore, you can simply pass a greater-than sign, and Swift can automatically infer that you want to use the string function implementation of the greater-than sign:\n\nimport Cocoalet names = ["AT", "AE", "D", "S", "BE"]var reversed = names.sorted(by: >)print(reversed)\nThe output of the above program execution is:\n\n["S", "D", "BE", "AT", "AE"]\n\n* * *\n\n## Trailing Closures\n\nA trailing closure is a closure expression written after the function's parentheses, which the function supports calling as its last argument.\n\nfunc someFunctionThatTakesAClosure(closure: () -> Void) { // function body part}// The following is not using a trailing closure to make the function call someFunctionThatTakesAClosure({ // closure body part})// The following is using a trailing closure to make the function call someFunctionThatTakesAClosure() { // closure body part}\n### Instance\n\nimport Cocoalet names = ["AT", "AE", "D", "S", "BE"]//trailing closure var reversed = names.sorted() { $0 > $1 }print(reversed)\nThe `{ $0 > $1}` after `sort()` is a trailing closure.\n\nThe output of the above program execution is:\n\n["S", "D", "BE", "AT", "AE"]\n> Note: If a function only requires a closure expression as its single parameter, when you use a trailing closure, you can even omit the `()`.\n> \n> reversed = names.sorted { $0 > $1 }\n\n* * *\n\n## Capturing Values\n\nClosures can capture constants or variables from the context in which they are defined.\n\nEven if the original scope in which these constants and variables were defined no longer exists, the closure can still reference and modify these values within the closure's function body.\n\nThe simplest form of a closure in Swift is a nested function, which is a function defined inside the body of another function.\n\nNested functions can capture all the parameters of their outer function as well as any constants and variables defined within it.\n\nLook at this example:\n\nfunc makeIncrementor(forIncrement amount: Int) -> () -> Int { var runningTotal = 0 func incrementor() -> Int { runningTotal += amount return runningTotal } return incrementor }\nA function `makeIncrementor`, which has an `Int` type parameter `amount`, and it has an external parameter name `forIncrement`, meaning you must use this external name when calling it. The return value is a `()-> Int` function.\n\nInside the function body, the variable `runningTotal` and the function `incrementor` are declared.\n\nThe `incrementor` function does not take any parameters, but it accesses the `runningTotal` and `amount` variables within its body. This is because it achieves this by capturing the `runningTotal` and `amount` variables that already exist in its containing function's body.\n\nSince the `amount` variable is not modified, `incrementor` actually captures and stores a copy of that variable, and this copy is stored along with `incrementor`.\n\nTherefore, when we call this function, it will accumulate:\n\nimport Cocoa func makeIncrementor(forIncrement amount: Int) -> () -> Int { var runningTotal = 0 func incrementor() -> Int { runningTotal += amount return runningTotal } return incrementor }let incrementByTen = makeIncrementor(forIncrement: 10)// The returned value is 10 print(incrementByTen())// The returned value is 20 print(incrementByTen())// The returned value is 30 print(incrementByTen())\nThe output of the above program execution is:\n\n102030\n\n* * *\n\n## Closures Are Reference Types\n\nIn the above example, `incrementByTen` is a constant, but the closure it points to can still increment the value of its captured variable.\n\nThis is because functions and closures are both reference types.\n\nWhether you assign a function/closure to a constant or a variable, you are actually setting the value of the constant/variable to be a reference to the corresponding function/closure. In the above example, the reference of `incrementByTen` pointing to the closure is a constant, not the closure's content itself.\n\nThis also means that if you assign a closure to two different constants/variables, both values will point to the same closure:\n\nimport Cocoa func makeIncrementor(forIncrement amount: Int) -> () -> Int { var runningTotal = 0 func incrementor() -> Int { runningTotal += amount return runningTotal } return incrementor }let incrementByTen = makeIncrementor(forIncrement: 10)// The returned value is 10 incrementByTen()// The returned value is 20 incrementByTen()// The returned value is 30 incrementByTen()// The returned value is 40 incrementByTen()let alsoIncrementByTen = incrementByTen // The returned value is also 50 print(alsoIncrementByTen())\nThe output of the above program execution is:\n\n50
← Swift EnumerationsSwift Functions β†’