* 내가 읽으려고 내 맘대로 번역한 글.
* 원문 : https://docs.swift.org/swift-book/LanguageGuide/Closures.html
Closures
Closures are self-contained blocks of functionality that can be passed around and used in your code. Closures in Swift are similar to blocks in C and Objective-C and to lambdas in other programming languages.
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에서 클로저는 c 와 objective-c의 블록과 비슷하고, 다른 언어에서의 lambda 와 비슷하다.
클로저는 그 안에서 상수나 변수를 캡쳐하고 참조를 저장할수 있다.
This is known as closing over those constants and variables. -> 이거 무슨 뜻인지 모르겠다.
swift는 캡쳐의 메모리 관리를 모두 알아서 처리한다.
NOTE
Don’t worry if you are not familiar with the concept of capturing. It is explained in detail below in Capturing Values.
잘 몰라도 괜찮아. 밑에서 설명할거야.
Global and nested functions, as introduced in Functions, are actually special cases of closures. Closures take one of three forms:
앞장에서 나왔던 전역함수와 중첩함수는 모두 클로저의 특별한 경우다.
클로저의 형태는 다음 세자중 하나다.
- Global functions are closures that have a name and do not capture any values.
- Nested functions are closures that have a name and can capture values from their enclosing function.
- Closure expressions are unnamed closures written in a lightweight syntax that can capture values from their surrounding context.
- 전역함수는 아무값도 캡쳐하지 않고, 이름을 가지는 클로저다.
- 중첩함수는 자신을 감싸고 있는 함수안에서 값을 캡쳐할수 있고, 이름을 가지는 클로저다.
- 클로저 표현식은 이름 없는 클로저이고, 주변 문맥에서 값을 캡쳐할수 있고, 경량화된 문법으로 쓰여졌다.
Swift’s closure expressions have a clean, clear style, with optimizations that encourage brief, clutter-free syntax in common scenarios. These optimizations include:
swift의 클로저 표현식은 일반적으로 깔끔하고, 스타일이 명확하고, 간결하게 최적화 되었고, 좌우간 좋다.
이 최적화는 다음을 포함한다.
- Inferring parameter and return value types from context
- Implicit returns from single-expression closures
- Shorthand argument names
- Trailing closure syntax
- 문맥으로 부터 매개변수와 리턴값의 타입을 유추함.
- 단일 표현식의 클로저로부터 암시적 반환.
- 매개변수 이름의 간략화.
- 후위 클로저 문법.
Closure Expressions
Nested functions, as introduced in Nested Functions, are a convenient means of naming and defining self-contained blocks of code as part of a larger function. However, it is sometimes useful to write shorter versions of function-like constructs without a full declaration and name. This is particularly true when you work with functions or methods that take functions as one or more of their arguments.
이전 챕터에서 소개한 중첩된 함수는 큰 함수의 일부로 독립된 코드 블록을 이름붙이고 정의하는 편리한 방법이다.
하지만 전체 선언과 이름붙이는거 없이 간략하게 함수 비슷한거를 만드는게 유용할때가 있다.
특히 매개변수로 함수를 받는 함수나 메소드를 만들때 특히 유용하다.
Closure expressions are a way to write inline closures in a brief, focused syntax. Closure expressions provide several syntax optimizations for writing closures in a shortened form without loss of clarity or intent. The closure expression examples below illustrate these optimizations by refining a single example of the sorted(by:) method over several iterations, each of which expresses the same functionality in a more succinct way.
클로저 표현식은 인라인클로저를 간략하게 만들수 있는 문법에 초점이 맞추어져 있다.
클로저 표현식은 명확성과 의도를 잃지 않으면서 클로저를 간략하게 쓸수있는 최적화된 여러개의 문법을 제공한다.
아래에 있는 예제들은 sorted(by:) 메소드를 여러번 반복하면서 점점 좋게 만들어 최적화들을 보여준다 (번역이 맞는지 모르겠네. ㅋ)
같은 기능을 점점 더 간결하게 표현한다.
The Sorted Method
Swift’s standard library provides a method called sorted(by:), which sorts an array of values of a known type, based on the output of a sorting closure that you provide. Once it completes the sorting process, the sorted(by:) method returns a new array of the same type and size as the old one, with its elements in the correct sorted order. The original array is not modified by the sorted(by:) method.
swift의 표준 라이브러리는 sroted(by:)라는 메소드를 제공하는데, 이는 너가 제공한 정렬기능의 클로저를 기반으로 배열을 정렬한다.
정렬이 끝나면 sorted(by:) 메소드는 원본 배열과 같은 타입이고 크기도 같은 새로운 배열을 알맞은 순서로 정렬해서 리턴한다.
sorted(by:) 메소드는 원본 배열을 수정하지 않는다.
The closure expression examples below use the sorted(by:) method to sort an array of String values in reverse alphabetical order. Here’s the initial array to be sorted:
아래의 클로저 표현식은 sorted(by:) 메소드를 사용해서 문자열 배열을 알파벳 역순으로 정렬한다.
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
The sorted(by:) method accepts a closure that takes two arguments of the same type as the array’s contents, and returns a Bool value to say whether the first value should appear before or after the second value once the values are sorted. The sorting closure needs to return true if the first value should appear before the second value, and false otherwise.
sorted(by:) 메소드는 클로저를 받는데. 이 클로저는 배열의 내용과 동일한 타입의 매개변수 2개를 받고, 첫번째 값이 두번째 값의 앞에 와야 하는지 뒤에 와야 하는지를 알려주는 Bool 값을 리턴해야 한다.
정렬하는 클로저는 첫번째 값이 두번째 값보다 앞에 와야 하면 true를 리턴하고, 아니면 false 를 리턴해야 한다.
This example is sorting an array of String values, and so the sorting closure needs to be a function of type (String, String) -> Bool.
이 예제는 문자열 배열을 정렬하고, 정렬하는 클로저는 (String, String) -> Bool 타입의 함수를 필요로 한다.
One way to provide the sorting closure is to write a normal function of the correct type, and to pass it in as an argument to the sorted(by:) method:
정렬하는 클로저를 제공하는 방법중 하나는 알맞은 타입의 일반 함수를 만들어서, sorted(by:) 메소드에 매개변수로 넣는것이다.
func backward(_ s1: String, _ s2: String) -> Bool {
return s1 > s2
}
var reversedNames = names.sorted(by: backward)
// reversedNames is equal to ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
If the first string (s1) is greater than the second string (s2), the backward(_:_:) function will return true, indicating that s1 should appear before s2 in the sorted array. For characters in strings, “greater than” means “appears later in the alphabet than”. This means that the letter "B" is “greater than” the letter "A", and the string "Tom" is greater than the string "Tim". This gives a reverse alphabetical sort, with "Barry" being placed before "Alex", and so on.
첫번째 문자열 s1 이 두번째 문자열 s2 보다 크면, backward(_:_:) 함수는 true를 리턴해서 s1이 s2보다 앞에 위치해야 한다는걸 알려준다.
문자열의 글자에서 '더크다' 라는건 '알파벳 순서로 뒤에 나온다' 를 의미한다.
이것은 "B"가 "A"보다 크고, "Tom"이 "Tim" 보다 크다는걸 의미한다.
이것은 알파벳 역순으로 "Barry"가 "Alex" 보다 앞에 위치하게 해준다.
However, this is a rather long-winded way to write what is essentially a single-expression function (a > b). In this example, it would be preferable to write the sorting closure inline, using closure expression syntax.
그렇지만 이것은 본질적인 단일 표현식 함수 (a > b)를 장황하게 작성한거다. (간단한걸 복잡하게 썼다는 뜻)
이 예제에서는 클로저 표현식 문법을 사용해서 정렬하는 클로저를 인라인 으로 작성하는게 좋다.
Closure Expression Syntax
Closure expression syntax has the following general form:
클로저 표현 문법은 아래의 일반적인 형식을 가진다.
{ (parameters) -> return type in
statements
}
The parameters in closure expression syntax can be in-out parameters, but they can’t have a default value. Variadic parameters can be used if you name the variadic parameter. Tuples can also be used as parameter types and return types.
클로저 표현식 문법의 매개변수들은 in-out이 될수 있지만, 기본값은 가질수 없다.
가변매개변수도 사용가능하다.
tuple은 매개변수에 사용가능하고, 리턴 타입에도 사용가능하다.
The example below shows a closure expression version of the backward(_:_:) function from above:
아래 예제는 위에 있는 backward(_:_:) 함수를 클로저 표현식으로 바꾼것이다.
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 > s2
})
Note that the declaration of parameters and return type for this inline closure is identical to the declaration from the backward(_:_:) function. In both cases, it is written as (s1: String, s2: String) -> Bool. However, for the inline closure expression, the parameters and return type are written inside the curly braces, not outside of them.
인라인 클로저의 매개변수와 리턴타입의 선언은 backward(_:_:) 함수의 선언과 동일하다.
둘다 (s1: String, s2: String) -> Bool 이다.
하지만 인라인 클로저 표현식의 매개변수와 리턴타입은 중괄호 {} 안에 쓰여졌고, 밖에 노출되지 않는다.
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:
클로저의 body는 in 키워드로 시작된다.
in 키워드는 클로저의 매개변수와 리턴타입의 정의가 끝났고, 클로저의 body가 시작된다는덜 표시한다.
이 클로저는 body 의 내용이 짧기 때문에 한줄에 쓸수도 있다.
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 } )
This illustrates that the overall call to the sorted(by:) 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.
sorted 메소드 호출이 동일하다. 괄호 한쌍이 매개변수를 감싸고 있다. 이제 매개변수는 인라인 클로저다.
Inferring Type From Context
Because the sorting closure is passed as an argument to a method, Swift can infer the types of its parameters and the type of the value it returns. The sorted(by:) method is being called on an array of strings, so its argument must be a function of type (String, String) -> Bool. This means that the (String, String) and Bool types do not need to be written as part of the closure expression’s definition. Because all of the types can be inferred, the return arrow (->) and the parentheses around the names of the parameters can also be omitted:
정렬 클로저가 매소드에 매개변수로 전달되기 때문에, swift는 매개변수의 타입과 리턴값의 타입을 추론할수 있다.
sorted(by:) 메소드는 문자열 배열에서 호출되고, 매개변수는 반드시 (String, String) -> Bool 타입의 함수여야 한다.
이건 (String, String) 과 Bool 이 클로저 표현식 정의의 일부로 작성될 필요는 없다는 뜻이다.
왜냐면 모든 타입이 추론가능하니까, 매개변수를 감싸고 있는 중괄호와 리턴타입 부분을 생략할수 있다.
reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )
It is always possible to infer the parameter types and return type when passing a closure to a function or method as an inline closure expression. As a result, you never need to write an inline closure in its fullest form when the closure is used as a function or method argument.
인라인 클로저를 함수나 메소드에 전달할때 매개변수 타입과 리턴타입을 언제나 추론할수 있다.
결론적으로 함수나 메소드의 매개변수로 사용되는 클로저를 작성할때 완전한 형태로 길게 작성할 필요가 없다.
(언제나 위 예제처럼 짧게 작성할수 있다는 뜻)
Nonetheless, you can still make the types explicit if you wish, and doing so is encouraged if it avoids ambiguity for readers of your code. In the case of the sorted(by:) method, the purpose of the closure is clear from the fact that sorting is taking place, and it is safe for a reader to assume that the closure is likely to be working with String values, because it is assisting with the sorting of an array of strings.
그럼에도 불구하고 너가 원한다면 명시적으로 만들수도 있다 (원하면 길게 작성해라)
그리고 너의 코드를 읽는 사람이 혼란을 피할수 있도록 하기 원하면 길게 작성해라.
Implicit Returns from Single-Expression Closures
Single-expression closures can implicitly return the result of their single expression by omitting the return keyword from their declaration, as in this version of the previous example:
단일표현식 클로저는 결과를 return할때 return 키워드를 생략할수 있다.
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
Here, the function type of the sorted(by:) method’s argument makes it clear that a Bool value must be returned by the closure. Because the closure’s body contains a single expression (s1 > s2) that returns a Bool value, there is no ambiguity, and the return keyword can be omitted.
여기서 sorted(by:) 메소드의 매개변수인 클로저는 Bool 값을 리턴할게 분명하다.
클로저의 body에는 Bool 값을 리턴하는 단일표현식 (s1 > s2) 을 가지고 있으므로, 혼동될일이 없어서, retuen 키워드 생략 가능.
Shorthand Argument Names
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.
swift는 자동으로 인라인 클로저의 매개변수에 단축형 이름을 부여한다.
$0, $1, $2... 식으로 매개변수의 값을 참조할수 있다.
If you use these shorthand argument names within your closure expression, you can omit the closure’s argument list from its definition, and the number and type of the shorthand argument names will be inferred from the expected function type. The in keyword can also be omitted, because the closure expression is made up entirely of its body:
너가 이 단축형 매개변수 이름을 클로저 표현식 안에서 사용하면, 클로저 선언부의 매개변수 리스트를 생략할수 있다.
그리고 단축형 매개변수의 수와 타입이 추론된다.
클로저 표현식이 모두 클로저 body로 구성되었기 때문에 in 키워드도 생략 가능하다.
reversedNames = names.sorted(by: { $0 > $1 } )
Here, $0 and $1 refer to the closure’s first and second String arguments.
Operator Methods
There’s actually an even shorter way to write the closure expression above. Swift’s String type defines its string-specific implementation of the greater-than operator (>) as a method that has two parameters of type String, and returns a value of type Bool. This exactly matches the method type needed by the sorted(by:) method. Therefore, you can simply pass in the greater-than operator, and Swift will infer that you want to use its string-specific implementation:
사실 위의 클로저 예제는 더 짧게 쓸수 있다.
swift의 String타입은 > 연산자에 문자열을 위한 특별한 메소드를 구현했는데, 이 메소드는 2개의 매개변수를 받고 Bool값을 리턴한다.
이것은 정확히 sorted(by:) 메소드가 필요로 하는 형태와 동일하다.
그래서 그냥 단순히 > 연산자만 넘겨도 되고, swift 가 알아서 해준다. (ㅋ 해석이 안되서...)
reversedNames = names.sorted(by: >)
For more about operator method, see Operator Methods.
Trailing Closures
If you need to pass a closure expression to a function as the function’s final argument and the closure expression is long, it can be useful to write it as a trailing closure instead. A trailing closure is written after the function call’s parentheses, even though it is still an argument to the function. When you use the trailing closure syntax, you don’t write the argument label for the closure as part of the function call.
너가 함수의 마지막 매개변수로 클로저 표현식을 넘겨야 하는데, 그 클로저가 길다면, 후위 클로저로 작성하는게 편리하다.
후위 클로저는 함수 호출의 괄호 뒤에 작성지만 여전히 함수의 매개변수다.
후위 클로저 문법을 사용할때, 매개변수 label을 적을 필요 없다.
func someFunctionThatTakesAClosure(closure: () -> Void) {
// function body goes here
}
// Here's how you call this function without using a trailing closure:
someFunctionThatTakesAClosure(closure: {
// closure's body goes here
})
// Here's how you call this function with a trailing closure instead:
someFunctionThatTakesAClosure() {
// trailing closure's body goes here
}
The string-sorting closure from the Closure Expression Syntax section above can be written outside of the sorted(by:) method’s parentheses as a trailing closure:
reversedNames = names.sorted() { $0 > $1 }
If a closure expression is provided as the function or method’s only argument and you provide that expression as a trailing closure, you do not need to write a pair of parentheses () after the function or method’s name when you call the function:
reversedNames = names.sorted { $0 > $1 }
Trailing closures are most useful when the closure is sufficiently long that it is not possible to write it inline on a single line. As an example, Swift’s Array type has a map(_:) method which takes a closure expression as its single argument. The closure is called once for each item in the array, and returns an alternative mapped value (possibly of some other type) for that item. The nature of the mapping and the type of the returned value is left up to the closure to specify.
후위 클로저는 한줄에 작성하기 힘든 긴 클로저에 매우 유용하다.
예를 들어, 배열에 map(_:) 이라는 메소드가 있는데 이는 클로저 표현식 하나의 매개변수만 받는다.
그 클로저는 배열의 아이템 하나 마다 호출되고, 대체 매핑된 값(다른 타입일수도 있음)을 리턴한다.
리턴된 매핑의 특성과 타입은 클로저에 의해 지정된다.
After applying the provided closure to each array element, the map(_:) method returns a new array containing all of the new mapped values, in the same order as their corresponding values in the original array.
각 요소에 대해 클로저를 적용한후에, map(_:) 메소드는 원래 배열의 요소와 같은 순서로 매핑된 새로운 요소들을 갖는 배열을 리턴한다.
Here’s how you can use the map(_:) method with a trailing closure to convert an array of Int values into an array of String values. The array [16, 58, 510] is used to create the new array ["OneSix", "FiveEight", "FiveOneZero"]:
숫자값 배열을 문자열 배열로 변환하기 위해, map(_:) 메소드에 후위 클로저를 사용하는 법이 여기 있다.
[16, 58, 510] 숫자배열에서 ["OneSix", "FiveEight", "FiveOneZero"] 배열을 만든다.
let digitNames = [
0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four",
5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
]
let numbers = [16, 58, 510]
The code above creates a dictionary of mappings between the integer digits and English-language versions of their names. It also defines an array of integers, ready to be converted into strings.
위의 코드는 숫자와 그 숫자의 영어이름으로 이루어진 dictionary를 만들었다.
그리고 문자열로 바꿀 숫자 배열도 선언했다.
You can now use the numbers array to create an array of String values, by passing a closure expression to the array’s map(_:) method as a trailing closure:
이제 후위 클로저를 배열의 map(_:) 메소드에 전달해서 숫자 배열을 문자열 배열로 만들수 있다.
let strings = numbers.map { (number) -> String in
var number = number
var output = ""
repeat {
output = digitNames[number % 10]! + output
number /= 10
} while number > 0
return output
}
// strings is inferred to be of type [String]
// its value is ["OneSix", "FiveEight", "FiveOneZero"]
The map(_:) method calls the closure expression once for each item in the array. You do not need to specify the type of the closure’s input parameter, number, because the type can be inferred from the values in the array to be mapped.
map(_:) 메소드는 배열의 각 요소마다 후위 클로저를 적용한다.
클로저 입력 매개변수 number에 대한 타입을 적을 필요는 없다.
왜냐면 배열의 요소에서 타입을 추론할수 있으니까.
In this example, the variable number is initialized with the value of the closure’s number parameter, so that the value can be modified within the closure body. (The parameters to functions and closures are always constants.) The closure expression also specifies a return type of String, to indicate the type that will be stored in the mapped output array.
이 예제에서 number 변수는 클로저의 매개변수 number 값으로 초기화 되고, 클로저 body 안에서 값이 변경된다.
(함수와 클로저에 전달되는 매개변수들은 항상 상수다)
클로저 표현식은 출력배열에 저장될 리턴 타입을 문자열로 지정하였다.
The closure expression builds a string called output each time it is called. It calculates the last digit of number by using the remainder operator (number % 10), and uses this digit to look up an appropriate string in the digitNames dictionary. The closure can be used to create a string representation of any integer greater than zero.
클로저 표현식은 호출될때마다 output 이라는 문자열을 만든다.
나머지 연산자를 사용해서 number의 마지막 자리를 계산하고, digitNames dictionary에서 적절한 문자열을 찾는다.
이 클로저는 0 보다 큰 정수의 문자열 표현을 만들수 있다.
NOTE
The call to the digitNames dictionary’s subscript is followed by an exclamation mark (!), because dictionary subscripts return an optional value to indicate that the dictionary lookup can fail if the key does not exist. In the example above, it is guaranteed that number % 10 will always be a valid subscript key for the digitNames dictionary, and so an exclamation mark is used to force-unwrap the String value stored in the subscript’s optional return value.
digitNames dictionary 의 키값에 ! 가 있다.
왜냐면 dictionary에서 해당 키가 없으면 찾기에 실패하니까 optional 값을 리턴한다.
위의 예에서 number % 10 은 항상 digitNames 에 있는 키라는걸 보장한다.
그래서 느낌표는 dictionary의 optional 리턴값에서 강제로 문자열 값을 꺼낸다.
The string retrieved from the digitNames dictionary is added to the front of output, effectively building a string version of the number in reverse. (The expression number % 10 gives a value of 6 for 16, 8 for 58, and 0 for 510.)
The number variable is then divided by 10. Because it is an integer, it is rounded down during the division, so 16 becomes 1, 58 becomes 5, and 510 becomes 51.
The process is repeated until number is equal to 0, at which point the output string is returned by the closure, and is added to the output array by the map(_:) method.
The use of trailing closure syntax in the example above neatly encapsulates the closure’s functionality immediately after the function that closure supports, without needing to wrap the entire closure within the map(_:) method’s outer parentheses.
Capturing Values
A closure can capture constants and variables from the surrounding context in which it is defined. The closure can then refer to and modify the values of those constants and variables from within its body, even if the original scope that defined the constants and variables no longer exists.
클로저는 자신이 포함된 context 주변에서 상수와 변수를 캡쳐할수 있다.
심지어 그 상수와 변수가 정의된 원래 범위가 더 이상 존재하지 않더라도,
그 클로저는 자신의 body 안에서 그 상수와 변수들을 참조하고 수정할수 있다.
In Swift, the simplest form of a closure that can capture values is a nested function, written within the body of another function. A nested function can capture any of its outer function’s arguments and can also capture any constants and variables defined within the outer function.
swift에서 값을 캡쳐할수 있는 가장 간단한 형태는 다른 함수의 body안에 쓰여진 중첩함수다.
중첩함수는 자신을 감싼 함수의 모든 매개변수와 상수와 변수를 캡쳐할수 있다.
Here’s an example of a function called makeIncrementer, which contains a nested function called incrementer. The nested incrementer() function captures two values, runningTotal and amount, from its surrounding context. After capturing these values, incrementer is returned by makeIncrementer as a closure that increments runningTotal by amount each time it is called.
여기 makeIncrementer라는 함수가 있고 incrementer라는 함수를 포함하고 있다.
중첩된 incrementer() 함수는 자신을 감싸고 있는 context에 있는 runningTotal, amount 두개의 변수에 접근할수 있다.
이 변수들을 캡쳐한 후에, makeIncrementer 가 호출될때마다 runningTotal 을 amount 만큼 증가시키는 incrementer 클로저를
리턴한다.
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
The return type of makeIncrementer is () -> Int. This means that it returns a function, rather than a simple value. The function it returns has no parameters, and returns an Int value each time it is called. To learn how functions can return other functions, see Function Types as Return Types.
makeIncrementer의 리턴타입은 () -> Int 다.
이건 단순히 값을 리턴하지 않고 함수를 리턴한다는 뜻이다.
리턴되는 함수는 매개변수가 없고, 호출시 Int 값을 리턴한다.
The makeIncrementer(forIncrement:) function defines an integer variable called runningTotal, to store the current running total of the incrementer that will be returned. This variable is initialized with a value of 0.
makeIncrementer(forIncrement:) 함수는 runningTotal 이라는 변수를 정수형으로 정의하여,
현재 incrementer가 리턴할 총 합계를 저장한다. 이 변수는 0으로 초기화 되었다.
The makeIncrementer(forIncrement:) function has a single Int parameter with an argument label of forIncrement, and a parameter name of amount. The argument value passed to this parameter specifies how much runningTotal should be incremented by each time the returned incrementer function is called. The makeIncrementer function defines a nested function called incrementer, which performs the actual incrementing. This function simply adds amount to runningTotal, and returns the result.
makeIncrementer(forIncrement:) 함수는 forIncrement라고 라벨링된 정수형 매개변수 하나를 받는다. 그 이름은 name이다.
그 매개변수는 리턴된 inicrementer 함수가 호출될때마다 얼만큼 runningTotal 값을 증가시킬지에 쓰인다.
makeIncrementer 함수는 실질적으로 증가 작업을 수행하는 incrementer 라는 중첩된 함수를 선언하였다.
이 함수는 단순히 runningTotal에 amount 를 더하고 그 결과를 리턴한다.
When considered in isolation, the nested incrementer() function might seem unusual:
격리수준을 고려할때, 중첩된 incrementer() 함수는 이상해 보인다.
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
The incrementer() function doesn’t have any parameters, and yet it refers to runningTotal and amount from within its function body. It does this by capturing a reference to runningTotal and amount from the surrounding function and using them within its own function body. Capturing by reference ensures that runningTotal and amount do not disappear when the call to makeIncrementer ends, and also ensures that runningTotal is available the next time the incrementer function is called.
incrementer() 함수는 아무런 매개변수도 없지만, 자신의 body안에서 runningTotal, amount 를 참조한다.
이건 주변을 감싸는 함수가 가지고 있는 runningTotal과 amount의 참조를 캡쳐함으로써 가능하고,
자신의 body안에서 사용할수 있다.
참조 캡쳐는 makeIncrementer 함수의 호출이 끝나도 runningTotal과 amount 변수가 사라지지 않도록 해주고,
runningTotal 변수가 다음번 incrementer 함수가 호출될때도 사용가능하도록 해준다.
NOTE
As an optimization, Swift may instead capture and store a copy of a value if that value is not mutated by a closure, and if the value is not mutated after the closure is created.
Swift also handles all memory management involved in disposing of variables when they are no longer needed.
최적화를 통해서, 클로저 안에서 값이 변하지 않고, 클로저가 생성된후에 값이 변하지 않는다면 그 값의 복사본을 챕쳐하고 저장한다.
swift는 더이상 필요하지 않은 변수를 정리하는 메모리 관리도 한다.
Here’s an example of makeIncrementer in action:
makeIncrementer 사용법
let incrementByTen = makeIncrementer(forIncrement: 10)
This example sets a constant called incrementByTen to refer to an incrementer function that adds 10 to its runningTotal variable each time it is called. Calling the function multiple times shows this behavior in action:
이 예제는 incrementByTen 이라는 상수에 실행될때마다 runningTotal 변수에 10을 더하는 incrementer 함수의 참조를 넣었다.
이 함수를 여러번 호출하면 어떻게 되는지 보여준다.
incrementByTen()
// returns a value of 10
incrementByTen()
// returns a value of 20
incrementByTen()
// returns a value of 30
If you create a second incrementer, it will have its own stored reference to a new, separate runningTotal variable:
두번째 incrementer를 만들면, 별도의 runningTotal 변수를 가지는 새로운 참조를 가지게 된다.
let incrementBySeven = makeIncrementer(forIncrement: 7)
incrementBySeven()
// returns a value of 7
Calling the original incrementer (incrementByTen) again continues to increment its own runningTotal variable, and does not affect the variable captured by incrementBySeven:
첫번째 incrementByTen을 다시 호출해도, incrementBySeven이 챕쳐한 runningTotal 변수에는 영향이 없다.
incrementByTen()
// returns a value of 40
NOTE
If you assign a closure to a property of a class instance, and the closure captures that instance by referring to the instance or its members, you will create a strong reference cycle between the closure and the instance. Swift uses capture lists to break these strong reference cycles. For more information, see Strong Reference Cycles for Closures.
클래스 인스턴스의 속성에 클로저를 할당하고, 그 클로저가 인스턴스의 참조를 캡쳐하거나 또는 인스턴스의 멤버를 캡쳐하면,
그 클로져와 인스턴스 사이에 강한 참조가 생긴다.
swift는 이러한 강한 참조 주기를 깨기위해 캡쳐리스트를 사용한다.
Closures Are Reference Types
In the example above, incrementBySeven and incrementByTen are constants, but the closures these constants refer to are still able to increment the runningTotal variables that they have captured. This is because functions and closures are reference types.
위의 예제에서, incrementBySeven 과 incrementByTen 은 상수다.
하지만 이 상수들이 참조하는 클로저는 여전히 자신이 캡쳐한 runningTotal 변수를 증가시킬수 있다.
함수와 클로저는 참조타입이기 때문이다.
Whenever you assign a function or a closure to a constant or a variable, you are actually setting that constant or variable to be a reference to the function or closure. In the example above, it is the choice of closure that incrementByTen refers to that is constant, and not the contents of the closure itself.
너가 함수나 클로저를 상수나 변수에 할당할때, 실제로는 그 상수나 변수에 함수나 클로저의 참조를 설정하는 것이다.
위의 예에서, incrementByTen이 참조하는 상수는 클로저다. 클로저 자체의 내용이 아니다.
(그러니까 참조값이 상수라는거고, 참조되는 클로저가 상수라는 말은 아니다)
This also means that if you assign a closure to two different constants or variables, both of those constants or variables refer to the same closure.
너가 하나의 클로저를 2개의 다른 상수나 변수에 할당하면, 그 2개의 상수나 변수는 같은 클로저를 참조한다는 뜻이다.
let alsoIncrementByTen = incrementByTen
alsoIncrementByTen()
// returns a value of 50
incrementByTen()
// returns a value of 60
The example above shows that calling alsoIncrementByTen is the same as calling incrementByTen. Because both of them refer to the same closure, they both increment and return the same running total.
위의 예제는 alsoIncrementByTen 을 호출하는것은 incrementByTen을 호출하는것과 같다는걸 보여준다.
왜냐면 둘다 같은 클로저를 참조하고, 둘다 increment 이며, 같은 running total을 리턴하니까.
Escaping Closures
A closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns. When you declare a function that takes a closure as one of its parameters, you can write @escaping before the parameter’s type to indicate that the closure is allowed to escape.
클로저가 다른 함수의 매개변수로 전달될때, 그 클로저는 함수를 escape 한다고 말하지만, 그 함수가 리턴된 뒤에 호출된다.
너가 클로저를 매개변수로 받는 함수를 선언할때는 매개변수 타입 앞에 @escaping 키워드를 적어서
그 클로저가 escape 허용된다는걸 알릴수 있다.
(이석우 추가 : 이거 정확한 의미를 모르겠다. 나중에 다시 읽어보고 수정하자. 대체 escape를 뭐로 번역해야 할까???)
One way that a closure can escape is by being stored in a variable that is defined outside the function. As an example, many functions that start an asynchronous operation take a closure argument as a completion handler. The function returns after it starts the operation, but the closure isn’t called until the operation is completed—the closure needs to escape, to be called later. For example:
클로저가 escape 할수 있는 한가지 방법은 함수 밖에서 선언된 변수에 저장되는 것이다.
예를 들어, 비동기로 동작하는 많은 함수들은 completion handler로 사용될 클로저 매개변수를 받는다.
이 함수는 동작을 시작한후에 리턴지만, 그 클로저는 동작이 완료되기 전에는 호출되지 않는다.
그래서 그 클로저는 나중에 호출되기 위해서 escape가 필요하다
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}
The someFunctionWithEscapingClosure(_:) function takes a closure as its argument and adds it to an array that’s declared outside the function. If you didn’t mark the parameter of this function with @escaping, you would get a compile-time error.
someFunctionWithEscapingClosure(_:) 함수는 매개변수로 전달받은 클로저를 함수 밖에서 선언된 배열에 추가한다.
너가 이 함수의 매개변수에 @escaping 키워드를 빼먹으면 컴파일 에러가 발생한다.
Marking a closure with @escaping means you have to refer to self explicitly within the closure. For example, in the code below, the closure passed to someFunctionWithEscapingClosure(_:) is an escaping closure, which means it needs to refer to self explicitly. In contrast, the closure passed to someFunctionWithNonescapingClosure(_:) is a nonescaping closure, which means it can refer to self implicitly.
클로저에 @escaping 을 붙이면 그 클로저안에서는 명시적으로 self를 참조해야 한다.
예를들어 아래의 코드에서, somFunctionWithEscapingClosure(_:) 함수에 전달된 클로저는 escaping 클로저니까
자신을 참조할때는 명시적으로 self를 붙여야 한다.
반대로 someFunctionWithNonescapingClosure(_:) 함수에 전달된 클로저는 escaping 클로저가 아니니까
자신을 참조할때 self를 붙이지 않아도 된다.
func someFunctionWithNonescapingClosure(closure: () -> Void) {
closure()
}
class SomeClass {
var x = 10
func doSomething() {
someFunctionWithEscapingClosure { self.x = 100 }
someFunctionWithNonescapingClosure { x = 200 }
}
}
let instance = SomeClass()
instance.doSomething()
print(instance.x)
// Prints "200"
completionHandlers.first?()
print(instance.x)
// Prints "100"
Autoclosures
An autoclosure is a closure that is automatically created to wrap an expression that’s being passed as an argument to a function. It doesn’t take any arguments, and when it’s called, it returns the value of the expression that’s wrapped inside of it. This syntactic convenience lets you omit braces around a function’s parameter by writing a normal expression instead of an explicit closure.
autoclosure 는 함수에 매개변수로 전달될 표현식을 감싸기위해 자동으로 만들어진 클로저다.
이건 아무런 매개변수를 가지지 않고, 호출되면 표현식을 감싸고 있는 값을 리턴한다.
이 편리한 구문은 명시적 클로저 대신에 일반적인 표현식으로 쓰여진 함수의 매개변수를 감싸는 중괄호를 생략할수 있게 해준다.
It’s common to call functions that take autoclosures, but it’s not common to implement that kind of function. For example, the assert(condition:message:file:line:) function takes an autoclosure for its condition and message parameters; its condition parameter is evaluated only in debug builds and its message parameter is evaluated only if condition is false.
autoclosure를 받는 함수를 호출하는것은 흔하지만, 그런 함수를 직접 만드는것은 흔하지 않다.
예를들어 assert(condition:message:file:line:) 함수는 condition과 message 매개변수에 autoclosure를 받는다.
condition 매개변수는 debug 빌드때만 평가되고 message 매개변수는 condition이 false 일때만 평가된다.
An autoclosure lets you delay evaluation, because the code inside isn’t run until you call the closure. Delaying evaluation is useful for code that has side effects or is computationally expensive, because it lets you control when that code is evaluated. The code below shows how a closure delays evaluation.
autoclosure는 평가를 지연시킬수 있다. 왜냐면 클로저를 호출할때까지 코드가 실행되지 않으니까.
지연된 평가는 부작용이 있거나 계산 비용이 많이드는 코드에 유용하다.
왜냐면 코드가 평가될때 너가 제어할수 있으니까.
밑의 코드는 클로저가 평가를 지연시키는걸 보여준다.
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count)
// Prints "5"
let customerProvider = { customersInLine.remove(at: 0) }
print(customersInLine.count)
// Prints "5"
print("Now serving \(customerProvider())!")
// Prints "Now serving Chris!"
print(customersInLine.count)
// Prints "4"
Even though the first element of the customersInLine array is removed by the code inside the closure, the array element isn’t removed until the closure is actually called. If the closure is never called, the expression inside the closure is never evaluated, which means the array element is never removed. Note that the type of customerProvider is not String but () -> String—a function with no parameters that returns a string.
You get the same behavior of delayed evaluation when you pass a closure as an argument to a function.
customersInLine 배열의 첫번째 요소가 클로저 안에 있는 코드에 의해 삭제되더라도,
실제 클로저가 호출되기 전에는 배열의 요소가 지워지지 않는다.
만약 클로저가 호출되지 않으면, 클로저 안의 표현식을 결코 평가되지 않고, 배열 요소는 결코 제거되지 않는다.
customerProvider 의 타입은 String 이 아니고 () -> String (매개변수가 없고, 문자열을 리턴하는 함수) 이라는걸 주목해라.
클로저를 함수의 매개변수로 전달하면 지연 평가화 동일한 동작을 얻을수 있다.
// customersInLine is ["Alex", "Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: () -> String) {
print("Now serving \(customerProvider())!")
}
serve(customer: { customersInLine.remove(at: 0) } )
// Prints "Now serving Alex!"
The serve(customer:) function in the listing above takes an explicit closure that returns a customer’s name. The version of serve(customer:) below performs the same operation but, instead of taking an explicit closure, it takes an autoclosure by marking its parameter’s type with the @autoclosure attribute. Now you can call the function as if it took a String argument instead of a closure. The argument is automatically converted to a closure, because the customerProvider parameter’s type is marked with the @autoclosure attribute.
위에 있는 serve(customer:) 함수는 고객의 이름을 리턴하는 클로저를 명시적으로 받는다.
아래에 있는 serve(customer:) 는 동일한 동작을 하지만, 명시적인 클로저를 받는대신,
매개변수 타입에 @autoclosure 속성을 써서 autoclosure 를 받는다.
그래서 그 함수를 String 매개변수를 받는것처럼 호출할수 있다.
매개변수 타입에 @autoclosure 속성 썼기때문에, 그 매개변수는 자동으로 클로저로 변환된다.
// customersInLine is ["Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: @autoclosure () -> String) {
print("Now serving \(customerProvider())!")
}
serve(customer: customersInLine.remove(at: 0))
// Prints "Now serving Ewa!"
NOTE
Overusing autoclosures can make your code hard to understand. The context and function name should make it clear that evaluation is being deferred.
autoclosure를 너무 남발하면 코드가 이해하기 어려워진다 (그냥 딱 봐도 그럴거 같다)
context와 함수 이름에 평가가 지연된다는걸 잘 알수 있도록 해야 한다.
If you want an autoclosure that is allowed to escape, use both the @autoclosure and @escaping attributes. The @escaping attribute is described above in Escaping Closures.
autoclosure가 escape 되길 원하면, @autoclosure 와 @escaping 속성을 둘다 넣어라.
// customersInLine is ["Barry", "Daniella"]
var customerProviders: [() -> String] = []
func collectCustomerProviders(_ customerProvider: @autoclosure @escaping () -> String) {
customerProviders.append(customerProvider)
}
collectCustomerProviders(customersInLine.remove(at: 0))
collectCustomerProviders(customersInLine.remove(at: 0))
print("Collected \(customerProviders.count) closures.")
// Prints "Collected 2 closures."
for customerProvider in customerProviders {
print("Now serving \(customerProvider())!")
}
// Prints "Now serving Barry!"
// Prints "Now serving Daniella!"
In the code above, instead of calling the closure passed to it as its customerProvider argument, the collectCustomerProviders(_:) function appends the closure to the customerProviders array. The array is declared outside the scope of the function, which means the closures in the array can be executed after the function returns. As a result, the value of the customerProvider argument must be allowed to escape the function’s scope.
위의 코드는 customerProvider 매개변수로 전달된 클로저를 호출하는 대신,
collectCustomerProviders(_:) 함수는 customerProviders 배열에 추가한다.
그 배열은 함수 영역 밖에서 선언되었기 때문에, 배열안에 있는 클로저들은 함수가 리턴된 후에 실행될수 있다.
결과적으로 customerProvider 매개변수는 함수의 범위를 escape 하도록 허용되어야만 한다.
'iOS 초보' 카테고리의 다른 글
[swift5.1번역] 9.Structures and Classes (0) | 2019.08.07 |
---|---|
[swift5.1번역] 8.Enumeration (0) | 2019.08.01 |
[swift5.1번역] 6.Functions (0) | 2019.07.09 |
[swift5.1번역] 5.Control Flow (0) | 2019.07.03 |
[swift5.1번역] 4.Collection Types (0) | 2019.07.01 |