# 함수형 프로그래밍 2 - 클로저 [스위프트] 🦩
클로저
맵, 필터, 리듀스
모나드
import Swift
let add: (Int, Int) -> Int
add = {(a:Int, b:Int) -> Int in
return a + b
}
let substract: (Int, Int) -> Int
substract = {(a:Int, b:Int) -> Int in
return a-b
}
let divide: (Int, Int) -> Int
divide = {(a:Int, b:Int) -> Int in
return a/b
}
// 함수 안에서 전달받은 method클로저를 호출한다
func calculate(a:Int, b:Int, method: (Int, Int)-> Int) -> Int {
return method(a, b)
}
var calculated: Int
calculated = calculate(a: 50, b: 10, method: add)
print(calculated) // 60
calculated = calculate(a: 50, b: 10, method: substract)
print(calculated) // 40
calculated = calculate(a: 50, b: 10, method: divide)
print(calculated) // 5
calculated = calculate(a: 50, b: 10, method: {(left: Int, right:Int) -> Int in
return left * right
})
print(calculated) // 500
// 후행클로저
calculated = calculate(a: 50, b: 10) { (left: Int, right:Int) -> Int in
return left * right
}
print(calculated)
// 반환타입은 위에 명시되어 있기 때문에 생략 가능
calculated = calculate(a: 50, b: 10, method: {(left: Int, right:Int) in
return left * right
})
print(calculated) // 500
// 단축 인자 이름
calculated = calculate(a: 50, b: 10, method: {
return $0 * $1
})
print(calculated) // 500
// 단축 인자 이름 + 후행 클로저
calculated = calculate(a: 50, b: 10) {
return $0 * $1
}
print(calculated) // 500
// 암시적 반환
calculated = calculate(a: 50, b: 10) {
$0 * $1
}
print(calculated) // 500
13. 클로저 Closure
참조Reference를 획득Capture할 수 있어서
변수나 상수의 클로징(잠금)이라고 한다.
- 이름이 있거나 없거나
- 값을 획득하거나 하지 않거나
13.1 기본 클로저
{ (매개변수들) -> 반환타입 in 실행코드 }
let names: [String] = ["wizplan", "eric", "yagom", "jenny"]
let reversed: [String] = names.sorted(by: { (first: String, second: String) -> Bool in
return first > second
})
print(reversed) // ["yagom", "wizplan", "jenny", "eric"]
13.2 후행 클로저
Trailing closure을 사용하려면 completion 함수가 가장 뒤에 나와야 한다
- 클로저의 타입을 정의해주고 클로저 함수를 따로 써준 후 함수를 호출하는 경우
// 1.url 구글을 받으면 completion함수 {}를 실행시킨다
// 2.completion에 string을 넣어준다
//2.
func getData(url: String, completion: ((String)-> Void)) {
completion("foo")
}
//1.
getData(url: "www.google.com") { string in
print(string) //foo
}
- 클로저 타입을 선언하고 실행할 함수도 함께 쓴 경우
// Bool타입의 파라미터를 받는 클로저 타입의 변수 completion
// ((Bool) -> Void) = { 실행함수 }
let completion: ((Bool) -> Void) = { value in
print(value)
}
completion(true) // true
completion(false) // false
APICaller에서 활용해보기
import Foundation
class APICaller {
static let shared = APICaller()
// 1.
func performRequest(with url: URL, completion: @escaping ((Result<Data, Error>)-> Void)) {
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else {
return
}
completion(.success(data)) //비동기로 실행되는 클로저
}
task.resume()
}
}
-
Escaping closure captures non-escaping parameter 'completion'
task의 completion handler에서 performRequest의 completion handler를 사용하기 때문에 escaping 해주어야 한다task가 완료되면 data, response, error를 사용하는 completion handler 함수가 정의된다
이를 performRequest가 completion handler를 사용해 Result타입의 매개변수(성공 또는 실패)를 넣어 함수를 정의한다
여기에서는 성공하면 data를 받아서 Void를 리턴해! 라는 뜻으로 사용되었다
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
guard let url = URL(string: "") else {
return
}
APICaller.shared.performRequest(with: url) { result in
switch result {
case .success(let data):
print(data)
case .failure(let error):
print(error)
}
}
}
}
13.3 클로저 표현 간소화
13.3.1 문맥을 이용한 타입 유추
let reversed: [String] = names.sorted() { (first, second) in
return first > second
}
13.3.2 단축 인자 이름
let reversed: [String] = names.sorted() {
return $0 > $1
}
13.3.3 암시적 반환 표현 (한 줄 일 때)
let reversed: [String] = names.sorted() { $0 > $1 }
13.3.4 연산자 함수(>)
>
가 함수 이름
public func > <T: Comparable>(lhs: T, rhs: T) -> Bool
let reversed: [String] = names.sorted(by: >)
13.4 값 획득 Variable Capture
13.5 클로저는 참조 타입
func makeIncrementer(forIncrement amount: Int) -> (() -> Int) {
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
let incrementByTwo: (() -> Int) = makeIncrementer(forIncrement: 2)
let sameWithIncrementByTwo: (() -> Int) = incrementByTwo
let first: Int = incrementByTwo() //2
let second: Int = sameWithIncrementByTwo() //4
13.6 탈출 클로저 @escaping 💥
https://docs.swift.org/swift-book/LanguageGuide/Closures.html
non-escaping closure
func runClosure(closure: () -> Void) {
closure()
}
- 클로저가
runClosure()
함수의closure
인자로 전달됨 - 함수 안에서
closure()
가 실행됨 runClosure()
함수가 값을 반환하고 종료됨
이렇게 클로저가 함수가 종료되기 전에 실행되기 때문에 closure는 Non-Escaping 클로저 입니다.
escaping closure
class ViewModel {
var completionhandler: (() -> Void)? = nil
func fetchData(completion: @escaping () -> Void) {
completionhandler = completion
}
}
- 클로저가
fetchData()
함수의completion
인자로 전달됨 - 클로저
completion
이completionhandler
변수에 저장됨 fetchData()
함수가 값을 반환하고 종료됨- 클로저
completion
은 아직 실행되지 않음
completion은 함수의 실행이 종료되기 전에 실행되지 않기 때문에 escaping 클로저, 다시말해 함수 밖(escaping)에서 실행되는 클로저 입니다.
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}
위 함수에서 인자로 전달된 completionHandler는 someFunctionWithEscapingClosure 함수가 끝나고 나중에 처리 됩니다. 만약 함수가 끝나고 실행되는 클로저에 @escaping 키워드를 붙이지 않으면 컴파일시 오류가 발생합니다.
이스케이핑 클로저 (Escaping Closures)
: 클로저를 함수의 파라미터로 넣을 수 있는데, 파라미터 타입 앞에 @escaping이라는 키워드를 명시해야 합니다.
- 함수 밖(함수가 끝나고)에서 실행되는 클로저
- 비동기로 실행되는 클로저
@escaping 를 사용하는 클로저에서는 self를 명시적으로 언급해야 합니다.
func someFunctionWithNonescapingClosure(closure: () -> Void) {
closure() // 함수 안에서 끝나는 클로저
}
class SomeClass {
var x = 10
func doSomething() {
someFunctionWithEscapingClosure { self.x = 100 } // 명시적으로 self를 적어줘야 합니다.
someFunctionWithNonescapingClosure { x = 200 }
}
}
let instance = SomeClass()
instance.doSomething()
print(instance.x)
// Prints "200"
completionHandlers.first?()
print(instance.x)
// Prints "100"
import UIKit
// task 내부에서 외부의 completion: ((Bool) -> Void) 함수를 사용하기 위해 @escaping을 붙여준다
func getData(completion: @escaping ((Bool) -> Void)) {
let task = URLSession.shared.dataTask(with: URL(string: "")!) { data, response, error in
guard data != nil else {
completion(false)
return
}
completion(true)
}
task.resume()
}
final class APICaller {
var isReady = false
func warmup() {
isReady = true
// array에 값이 있으면 안에 있는 모든 completion을 실행시키고 삭제한다
if !completionHandlers.isEmpty {
completionHandlers.forEach { $0() }
completionHandlers.removeAll()
}
}
var completionHandlers = [(() -> Void)]()
func doSomething(completion: @escaping (() -> Void)) {
guard isReady else {
completionHandlers.append { //1. 1. 외부의 completionHandlers를 사용하기 때문에 @escaping이 필요하다
completion()
}
return
}
completion()
}
}
클로저를 활용해 버튼 생성해보기
버튼 생성할 때 클로저를 활용하면 속성을 한눈에 보기 쉬워진다
import UIKit
class ViewController: UIViewController {
// let button = UIButton()
// 파라미터는 없지만 버튼을 리턴하는 함수
let button: UIButton = {
let button = UIButton()
button.backgroundColor = .red
button.setTitleColor(.white, for: .normal)
button.setTitle("static configure", for: .normal)
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
//configureButton()
}
// private func configureButton() {
// button.backgroundColor = .gray
// button.setTitle("Continue", for: .normal)
// }
@objc func didTapButton() {
}
}
Author And Source
이 문제에 관하여(# 함수형 프로그래밍 2 - 클로저 [스위프트] 🦩), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@msi753/함수형-프로그래밍-클로저저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)