소소한개발팁
article thumbnail
반응형

1. Closures

- Closure는 코드 블록으로 C와 Objective-C의 블록과 다른 언어의 람다와 비슷합니다.

  클로저는 어떤 상수나 변수의 참조를 캡처하여 저장할 수 있습니다.

  Swift는 이 캡처와 관련한 모든 메모리를 자동으로 처리합니다.

 

- 클로저의 형태

  전역 함수 : 이름이 있고 어떤 값도 캡처하지 않는 클로저

  중첩 함수 : 이름이 있고 관련한 함수로부터 값을 캡처할 수 있는 클로저

  클로저 표현 : 경량화된 문법으로 쓰이고 관련된 문맥으로부터 값을 캡처할 이름 이 없는 클로저

 

 

2. Closure Expression Syntax  

- closure의 기본적인 예시입니다.

 

<swift />
{ (parameters) -> return type in statements }

 

- closure를 적용하지 않은 변수와 적용시킨 변수의 예시입니다.

 

<swift />
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"] reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 })

 

- closure 문의 길이가 짧을 경우 아래와 같이 축약할 수 있습니다.

 

<swift />
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 } )

 

- closure 문 안에서 타입 유형을 지정하지 않아도 Swift 유형 추론으로 인하여 처리됩니다. 

 

<swift />
reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )

 

- closure 문 안에서 return 값을 생략할 수 있습니다.

 

<swift />
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )

 

- Shorthand Argument Names을 사용하여 아예 변수명을 생략할 수 있습니다.

 

<swift />
reversedNames = names.sorted(by: { $0 > $1 } )

 

- 또는 단순히 Operator Methods를 사용해서 표현하더라도 유추를 통하여 처리될 수 있습니다.

 

<swift />
reversedNames = names.sorted(by: >)

 

 

3. Trailing Closures 

- 아래는 Trailing Closures의 예시입니다.

 

<swift />
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 }

 

- 아래는 Trailing Closures를 이용하여 배열안에 있는 숫자를 문자열로 출력하는 예시입니다.

 

<swift />
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] 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"]

 

 

- 아래는 Trailing Closures 를 이용하여 사진을 로드할 때 사용하는 함수를 처리하는 예시입니다.

 

<swift />
func loadPicture(from server: Server, completion: (Picture) -> Void, onFailure: () -> Void) { if let picture = download("photo.jpg", from: server) { completion(picture) } else { onFailure() } } loadPicture(from: someServer) { picture in someView.currentPicture = picture } onFailure: { print("Couldn't download the next picture.") }

 

 

4. Capturing Values 

- 아래는 단순히 값을 반환하는 게 아닌 함수를 반환하는 것의 예시입니다.

 

<swift />
func makeIncrementer(forIncrement amount: Int) -> () -> Int { var runningTotal = 0 func incrementer() -> Int { runningTotal += amount return runningTotal } return incrementer }

 

- 아래는 위의 함수를 반환하는 함수를 선언하고 사용한 예시입니다.

 

<swift />
let incrementByTen = makeIncrementer(forIncrement: 10) incrementByTen() // returns a value of 10 incrementByTen() // returns a value of 20 incrementByTen() // returns a value of 30.

 

- 아래는 위의 함수를 반환하는 함수를 인자 값을 변경하여 새로 선언하고 사용한 예시입니다.

 

<swift />
let incrementBySeven = makeIncrementer(forIncrement: 7) incrementBySeven() // returns a value of 7

 

- 원래의 함수를 실행시켰을 때 값이 변하지 않았던 것을 확인할 수 있습니다.

 

<swift />
incrementByTen() // returns a value of 40

 

 

 

5. Closures Are Reference Types 

- alsoIncrementByTen라는 상수가 incrementByTen 상수와 동일하기 때문에 같은 곳을 참조하는 것을 확인할 수 있습니다.

 

<swift />
let alsoIncrementByTen = incrementByTen alsoIncrementByTen() // returns a value of 50 incrementByTen() // returns a value of 60

 

 

6. Escaping Closures 

- 클로저를 함수의 파라미터로 넣을 수 있는데, 함수 밖에서 실행되는 클로저 예를 들면, 비동기로 실행되거나 completionHandler로 사용되는 클로저는 파라미터 앞에 @escaping이라는 키워드를 명시해야 합니다.

 

<swift />
var completionHandlers: [() -> Void] = [] func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) { completionHandlers.append(completionHandler) }

 

- @escaping를 사용하는 클로저에서는 self를 명시적으로 언급해야 합니다.

 

<swift />
func someFunctionWithNonescapingClosure(closure: () -> Void) { closure() } class SomeClass { var x = 10 func doSomething() { someFunctionWithEscapingClosure { self.x = 100 } //self !! someFunctionWithNonescapingClosure { x = 200 } } }
<swift />
let instance = SomeClass() instance.doSomething() print(instance.x) // Prints "200" completionHandlers.first?() print(instance.x) // Prints "100"
<swift />
class SomeOtherClass { var x = 10 func doSomething() { someFunctionWithEscapingClosure { [self] in x = 100 } someFunctionWithNonescapingClosure { x = 200 } } } struct SomeStruct { var x = 10 mutating func doSomething() { someFunctionWithNonescapingClosure { x = 200 } // Ok someFunctionWithEscapingClosure { x = 100 } // Error } }

 

 

7. Autoclosures

- 자동 클로저는 인자 값이 없으며 특정 표현을 감싸서 다른 함수에 전달 인자로 사용할 수 있는 클로저입니다. 자동클로저는 클로저를 실행하기 전까지 실제 실행이 되지 않습니다. 그래서 계산이 복잡한 연산을 하는데 유용합니다. 그 이유는 실제 계산이 필요할 때 호출되기 때문입니다. 아래는 예제입니다.

 

<swift />
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"

 

-  자동 클로저는 적힌 라인 순서대로 바로 실행되지 않고, 실제 사용될 때 지연 호출됩니다. 아래는 자동 클로저를 함수로 넣을 경우의 예시입니다.

 

<swift />
// 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!"

 

- serve함수는 인자가 없고, String을 반환하는 클로저를 받는 함수입니다. 이 함수를 실행할 때는 serve(customer: { customersInLine.remove(at: 0) } ) 이와 같이 클로저{ customersInLine.remove(at: 0) }를 명시적으로 직접 넣을 수 있습니다. 아래는 @autoclosure를 이용하여 축약시킨 예시입니다.

 

<swift />
// 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!"

 

- 클로저 인자에 @autoclosure를 선언하면 함수가 이미 클로저 인 것을 알기 때문에 리턴 값 타입과 같은 값을 넣어줄 수 있습니다. 아래는 @escaping과 @autoclosure를 같이 사용한 예시입니다.

 

<swift />
// 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!"

 

- collectCustomerProviders함수의 인자 customerProvider는 @autoclosure이면서 @escaping로 선언되었습니다. @autoclosure로 선언됐기 때문에 함수의 인자로 리턴값 String만 만족하는 customersInLine.remove(at: 0) 형태로 함수 인자에 넣을 수 있고, 이 클로저는 collectCustomerProviders함수가 종료된 후에 실행되는 클로저 이기 때문에 인자 앞에 @escaping 키워드를 붙여주었습니다.

 

 

 

내용은 https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html를 보면서 작성하였고 원문으로 작성된 내용을 옮기다 보니 이상한 부분이 있을 수 있습니다. 자세한 내용은 위의 링크를 확인해주시기 바랍니다.

반응형
profile

소소한개발팁

@개발자 뱅

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!