본문 바로가기
🌱

Closure 1/3

by 방우 2022. 10. 28.

어떠한 기능을 하는 이름있는 코드블럭 -> 함수

어떠한 기능을 하는 이름없는 코드블럭 -> 클로저

 

목차

  • Closure?
  • vs. Function
  • Closure Expression 
  • Capture
  • Escaping / Non-Escaping Closure
  • AutoClosure
  • swift 5.7 

 

Closure

클로저란 어떤 기능을 하는 코드를 하나의 블럭으로 모아놓은 것이다. 
선언할 당시의 환경을 기억했다가 
나중에 호출 될 시 원래의 환경에 따라 호출되는 코드블럭이다

 

함수는 이름있는 클로저(feat. 공식문서)

1. 전역함수는 이름이 있고 값을 캡처하지 않는 클로저
  함수 == 글로벌 스코프(전역)
2. 중첩함수는 이름이 있고 둘러싸는 함수에서 값을 캡처할 수 있는 클로저
  메서드 == 형식 내부(클래스, 구조체, 열거형)선언
3. 클로저 표현식은 주변 컨텍스트에서 값을 캡처할 수 있는 간단한 구문으로 작성된 이름 없는 클로저 이름이 없는 함수

클로저는? 이름 없는 코드 블럭(익명함수)
왜 이름을 적지않을까?
- 함수처럼 선언해놓고 호출하는 방식이 아니라 필요할 때마다 코드 블럭을 작성하는 방식

 

 

그렇다면 풩션이 아니라 왜 클로저를 사용하는건데?

둘다 일급객체이고 작동원리가 비슷하면 func을 써도 되잖아?

 

 

하지만

 

1. 함수 스코프를 벗어나서 사용할 수 있으며

2. callBack으로 네트워크통신의 비동기작업을 핸들링 할 수 있고

3. 효율적

 

1. 함수의 범위를 벗어나서 사용할 수 있다 -> @escaping에서 설명할 예정 

 

2. CallBack

 

부르고 받아서 백처리(저장)한다. 누가? 컴플리션핸들러가(클로저)

알라모파이어가 백그라운드에서 통신을 동안에, 비동기처리에 의해 스레드는 해당작업을 기다리지 않고 다음 작업을 시작한다.

콜백을 사용하면 응답값을 받아올 수 있다.

 

 

🤔 그래도 함수로 구현할 수 있지 않나?

 

1. 알라모파이어가 통신 2. 그 응답값을 받아서 외부로 반환 

이렇게 두 가지 기능으로 나눠볼 수 있을 것 같다

두 가지를 중첩함수 혹은 반환값을 받아 별개의 함수로 구현하더라도

작업의 비용을 생각했을 때 클로저가 더 적합할 것 같다 

 

3.  간편 및 효율적

위의 UIView 보면  클로저 안의 구성요소를 별도의 함수로 빼고 클로저 안에 실행시킬 수도 있다. 

함수로 구현했을 때 보다 간결한 표현이 가능하기 때문에 리소스를 줄일 수 있다.

 

 

4. 일회성(내 생각)

클로저가 일회성인 이유를 생각해본다면.

이름있는 함수는 해당 구분의 재사용성을 기대할 수 있다. 함수를 하나의 기능을 다양한 곳에서 호출함으로써 공통적인 기능을 분리할 수 있는 것이다. 하지만 클로저는 하나의 기능 필요할 때 블럭을 만들어 쓴기 때문에 상대적으로 일회적인 특성을 지니는 것 같다

 


 

클로저의 표현식

클로저 표현 문법(함수과 같은 기능임)
{ (parameters) -> return type in
    statements // in 다음에 나오는 statements를 body라고 부름
}

 

인라인클로저

함수로 따로 정의된 형태가 아닌 인자로 들어가 있는 형태의 클로저를 인라인 클로저 

우리가 흔히 아는 클로저의 형태이다

명확성이나 의도의 손실 없이 짧은 형식으로 클로저를 작성하기 위한 여러구문 최적화 제공한다.

 

 in 키워드는 클로저의 매개변수 반환 유형 정의가 완료되었고 클로저 본문이 시작될 것임을 나타낸다

func sorted(by areInIncreasingOrder: (Self.Element, Self.Element) throws 
-> Bool ) rethrows -> [Self.Element])

 

1. 함수 예

let numbers = [1,2,3,4] 
func backward(_ s1:Int, _ s2:Int ) -> Bool {return s1 > s2 } 
var reversedNumbers = numbers.sorted(by: backward)

 

2. 클로저 예

let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"] 
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 })
(따로 함수를 만들필요없이 클로저를 넣고 sorted함수가 실행되면서 클로저가 호출(선언)되고, 
정렬이 시작됨)

 

표현식 축약 과정

문맥에서 타입 추론

  • sorted(by:)는 배열의 Element와 같은 타입을 같고 두개의 인자(s1, s2)를 갖는다.
  • 이미 sorted(by:)의 메소드에서 (String, String) -> Bool 타입의 인자가 들어와야하는지 알기 때문에 클로저에서 이 타입들을 생략할 수 있다.

인라인 클로저 표현식으로 함수나 메서드에 클로저를 전달할 때 매개변수 유형과 반환 유형을 항상 유추할 수 있습니다. 결과적으로 클로저가 함수 또는 메서드 인수로 사용될 때 인라인 클로저를 완전한 형태로 작성할 필요가 없습니다. 그러나 가독성과 코드의 모호성을 파하기 위해 타입을 명시할 수 있고, 애플은 이를 권장하고 있습니다

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

 

단일 표현 클로저에서의 암시적 반환

  • 단일 표현 클로저에서는 반환 키워드를 생략할 수 있다.
  • 어떠한 모호성도 없으며 두 인자를 받아 두 값을 비교한 결과를 반환한다
reversedNames = names.sorted(by: { (s1, s2) in s1 > s2
})

 

인자 이름 축약

swift는 인라인 클로저에 자동으로 축약 인자 이름을 제공한다. 이 인자를 사용하면 인자 값을 순서대로 $0, $1, $2등으로 사용할 수 있다. 축약 인자 이름을 사용하면, 매개변수와 그 인자로 처리할 때 사용하는 인자 (body에서 사용)가 같다는 것을 알기 때문에 인자를 입력받는 부분과 in 키워드 부분을 생략할 수 있고 → 축약어를 사용할 수있게 된다.

  • 아래를 해석하면 1. $0과 $1 인자를 두개 받아서 2. $0이 $1 보다 큰지를 비교하고 3. 그 결과(Bool)를 반환해라
reversedNames = names.sorted(by: { $0 > $1 } )

 

연산자 메소드

swift String타입 연산에는 String끼리 비교할 수 있는 비교 연산자(>)를 구현할 수 있고, 그래서 아래와 같이 표현 가능하다

reversedNames = names.sorted(by: >)

 

후행클로저

  • 클로저 표현식을 함수에서 끝 매개변수로 전달해야하고 클로저 표현식이 긴 경우 후행클로저로 작성하는 것이 유용하다
  • 후행 클로저 구문을 사용할 때 함수 호출의 일부로 첫 번째 클로저에 대한 인수 레이블을 작성하지 않는다.  함수 호출에는 여러 개의 후행 클로저가 포함될 수 있다.
  • 그러나 아래의 처음 몇 가지 예에서는 단일 후행 클로저를 사용한다
//클로저가 유일한 매개변수일 때
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
}
  • ()클로저 표현식이 함수 또는 메서드의 유일한 인수로 제공되고 해당 표현식을 후행 클로저로 제공하면. 함수 혹은 메서드 이름 뒤에 한 쌍의 괄호를 작성할 필요가 없다. sorted(by:)
reversedNames = names.sorted { $0 > $1 }
  • 후행 클로저는 클로저가 충분히 길어서 한 줄에 인라인으로 작성할 수 없을 때 유용함.
  • 클로저가 2개라면?
    • 이 방법으로 함수를 작성하면 두 가지 상황을 모두 처리하는 하나의 클로저를 사용하는 대신,
      성공적인 다운로드 후 사용자 인터페이스를 업데이트하는 코드에서 네트워크 오류 처리를 담당하는 코드를 명확하게 분리할 수 있다.
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.")
}

'🌱' 카테고리의 다른 글

Closure 3/3 - 고차함수  (1) 2022.10.29
Closure 2/3  (0) 2022.10.29
값전달 - 클로저 트러블 슈팅  (0) 2022.08.19
Equatable  (0) 2022.08.18
forEach와 for_in 차이  (0) 2022.08.17