Swift - Closures 3

 

Swift의 Closures 정리 3 - Trailing Closures(후행 클로져)

Closures #3


Trailing Closures(후행 Closures)

  • 길이가 긴 closure 표현식을 함수의 마지막 인자로 전달하고자 할 경우 trailing closure를 사용하는 것이 더 유용하다.
  • 함수의 파라미터이지만 trailing closure는 파라피터가 포함된 괄호 밖에 쓴다.
  • trailing closure 문법을 사용할 경우 첫 번째 closure에 대한 argument label을 적을 필요가 없다.
  • 함수 호출 시 여러개의 trailing closure를 포함할 수 있다.
func someFunctionThatTakesAClosure(closure: () -> Void) {
	// function body
}

someFunctionThatTakesAClosure(closure: {
	// closure's body
})

someFunctionThatTakesAClosure() {
	// trailing closure's
}
  • 앞서 sorted(by:) 메서드의 예제는 다음과 같이 trailing closure로 쓸 수 있다.
reversedNames = names.sorted() {$0 > $1}
  • 만일 closure 표현식이 함수나 메서드의 유일한 인자로 전달되고 trailing closure라면 함수나 메서드 이름 뒤의 괄호를 생략할 수 있다.
reverseNames = names.sorted { $0 > $1 }
  • trailing closure는 closure가 한 문장의 inline 표현식으로 사용되지 못할만큼 긴 경우 유용하다.
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 // 파라미터로 trailing closure 전달
	var number = number
	var output = ""
	repeat {
		// 딕셔너리 검색 시 키가 없는 경우 nil이 반화될 수 있기 때문에 딕셔너리의 원소 값은 옵셔널로
		// 반환된다. 하지만 이 코드에서는 키 값이 반드시 존재하는 상황이어서 force-unwrap하고 있음
		output = digitNames[number % 10]! + output
		number /= 10
	} while number > 0
	return output
}
  • 위의 코드는 Swift의 Array에 있는 map(_:) 함수에 대한 예이다.
    • map(_:) 함수는 하나의 closure 표현식을 인자로 받는다.
    • closure는 배열의 각 item마다 한번씩 호출되며 인자로 전달된 아이템에 대한 값을 반환한다(type은 다를 수도 있다).
    • 인자로 전달된 closure의 코드를 작성하여 mapping의 특성과 반환 type을 지정할 수 있다.
    • closure가 배열의 모든 요소에 대해 작업을 완료하면 map(_:) 함수는 원래 배열의 값과 대응되며 동일한 수서를 갖는 새로운 배열을 반환한다.
    • 매핑될 배열로부터 type이 자동 추론되므로 closure에 파라미터 type을 명시할 필요는 없다.
    • 위 예제와 같이 trailing closure는 함수의 괄호로 둘러쌀 필요가 없다.
  • 함수가 여러개의 trailing closure를 취하는 경우 함수 호출 시 첫 번째 trailing closure는 argument label을 생략할 수 있고 나머지 trailing closure에는 label을 붙여야 한다.
func loadPicture(from server: Server, completion: (Picture) -> Void, onFailure: () -> Void) {
	if let picture == download("photo.jpg", from: server) {
		completion(picture)
	} else {
		onFaliure()
	}
}
  • 위 함수를 호출할 때 2개의 closure를 넘겨야 한다. 하나는 completion handler로 다운로드가 성공한 후 사진을 보여주는 closure이고, 다른 하나는 error handler로 사용자에게 error 내용을 보여준다.
loadPicture (from: someServer) { picture in	// 첫 번째 closure는 argument label 생략
	someView.currentPicture = picture
} onFailure: {					// 두 번째 closure는 argument label 사용
	print("Couldn't download the next picture.")
}