본문 바로가기

Apple/Swift

Swift 02 - 클로저(Closure)

반응형

❑ 클로저

Swift에서는 문자열(String)이나 정수(Int)와 같은 다른 타입들처럼 타입으로 함수를 사용할 수 있다. 이 말은 함수를 만들어 변수에 할당하고 해당 변수를 사용해 함수를 호출하고 해당 함수를 매개변수로 다른함수에 저장하는 등의 기능을 할 수 있다는 뜻이다. 이런 식으로 사용하는 함수를 Closure(클로저)라고 한다. 함수 동작 방식과 유사하지만 다른 점이 있다. SwiftUI에서 클로저 정말 많이 사용하게 된다.

let sprint = {
    print("손흥민이 스프린트한디.")
}

sprint()

변수에 할당하는 클로저는 함수(function)와는 달리 따로 이름이 없고 변수에 그대로 할당한다. 할당한 변수명을 함수와 같이 호출하여 사용할 수 있다.

작성된 클로저를 보면 파라미터(parameter: 매개변수)를 입력할 괄호나 공간이 없다는 것을 확인할 수 있다. 클로저는 함수와는 다른 형태로 파라미터를 작성한다. 클로저의 파라미터는 중괄호 안에 작성되며 파라미터 뒤에 in 키워드를 작성하여 클로저의 내용이 시작한다는 것을 표시한다. 함수와는 달리 파라미터 이름을 작성하지 않고 사용한다는 것이다(작성하면 에러가 난다. 파라미터 이름 쓰면 안됨)

let sprint = { (name: String) in
    print("\(name)이 스프린트한다.")
}

sprint("손흥민")

//output
손흥민이 스프린트한다.

 

클로저는 함수와 같이 값을 반환할 수 있다. 반환하는 타입 작성 방법은 함수와 같다.

let sprint = { (name: String) -> String in
    return "\(name)이 스프린트한다."
}

let content = sprint("손흥민")
print(content)

//output
//손흥민이 스프린트한다.

 

❑ 함수의 파라미터로 사용하는 클로저

클로저는 함수의 파라미터로도 사용할 수 있다. 기본적인 사용방법은 아래와 같다.

// 클로저 선언
let sprint = {
    print("손흥민이 스프린트 중...")
}

// 클로저를 파라미터로 하는 함수 작성, () -> Void 가 클로저를 파라미터로 한다는 뜻, Void는 Swift에서 아무것도 반환하지 않는다는 뜻
func secondHalf(sprint: () -> Void){
    print("베르통언의 패스...")
    sprint()
    print("골!!!")
}

// sprint를 secondHalf 함수의 파라미터로 호출
secondHalf(sprint: sprint)

//output
//베르통언의 패스...
//손흥민이 스프린트 중...
//골!!!

 

❑ Trailing closure syntax

함수의 마지막 파라미터가 클로저인 경우, Trailing closure syntax(트레일링 클로저 구문?)이라는 방식을 사용할 수 있다. 클로저를 매개변수로 전달하는 것이 아니라 함수의 중괄호 내용으로 작성하는 것이 바로 그 방법이다.

// 클로저 선언
let sprint = {
    print("손흥민이 스프린트 중...")
}

// 함수 선언
func secondHalf(sprint: () -> Void){
    print("베르통언의 패스...")
    sprint()
    print("골!!!")
}

// 함수의 중괄호 내부에서 클로저 호출
secondHalf{
    sprint()
}

//output
//베르통언의 패스...
//손흥민이 스프린트 중...
//골!!!

 

❑ 함수 선언시 자체 파라미터를 받는 클로저의 사용

함수에 전달된 클로저는 자체 파라미터도 받을 수 있다. 여기부터 조금 헷갈릴 수 있는데 다른 내용으로 이것저것 작성해 보면 금방 익숙해 질 수있다.

func secondHalf(sprint: (String) -> ()){
    print("베르통언의 패스...")
    // 문자열("손흥민")을 파라미터로 받는 클로저를 호출
    sprint("손흥민")
    print("골!!!")
}

//선언한 함수에서 trailing 클로저 구문 사용하여 secondHalf 호출
secondHalf{ (name: String) in
    print("\(name)이 스프린트 중...")
}

//output
//베르통언의 패스...
//손흥민이 스프린트 중...
//골!!!

 

❑ 클로저를 매개변수로 하는 함수의 선언

값을 반환하는 클로저를 매개변수로 하는 함수 또한 만들 수 있다.

// 문자열을 파라미터로 받고 문자열을 반환하는 클로저를 파라미터로 하는 함수의 선언
func secondHalf(sprint: (String) -> (String)){
    print("베르통언의 패스...")
    let content = sprint("손흥민")
    print(content)
    print("골!!!")
}

// 함수 호출
secondHalf{ (name: String) -> String in
    return "\(name)이 스프린트 중..."
}

//output
//베르통언의 패스...
//손흥민이 스프린트 중...
//골!!!

 

❑ 클로저 사용의 축약표현

Swift는 간결한 코드를 지향하는데 클로저 사용방법에서 그 방향성을 엿볼 수 있다. 위에 작성한 코드 중 함수를 호출하는 부분을 굉장히 축약해 표현이 가능하다.

0. 원본 함수 호출 방법

secondHalf{ (name: String) -> String in
    return "\(name)이 스프린트 중..."
}

1. 파라미터 타입 제거(괄호와 타입 제거)

secondHalf{ name -> String in
    return "\(name)이 스프린트 중..."
}

2. 반환타입 제거( -> String 제거) : Swift는 반환 타입을 알고 있다.

secondHalf{ name in
    return "\(name)이 스프린트 중..."
}

3. return 키워드 제거(!) : 클로저는 값을 반환하는 코드를 하나만 갖기 때문에 return 키워드를 제거 할 수 있다.

secondHalf{ name in
    "\(name)이 스프린트 중..."
}

4. 자동 파라미터 이름 제공 문법( name in 제거, name을 $0 으로 변경, 파라미터의 순서대로 0, 1, 2로 작성하면 된다.)

secondHalf{ 
    "\($0)이 스프린트 중..."
}

 

여러 개의 파라미터의 클로저 축약 방법

// 여러 개의 파라미터를 가진 클로저를 파라미터로 하는 함수 선언
func vsBunleyGoalByHM7(sprint: (String, String) -> String) {
    let content = sprint("베르통언", "손흥민")
    print(content)
    print("골!!!")
}

// 함수 호출 방법
vsBunleyGoalByHM7 {
    "\($0)의 절묘한 패스 후에 이어지는 \($1)의 스프린트..."
}

// output
// 베르통언의 절묘한 패스 후에 이어지는 손흥민의 스프린트...
// 골!!!

 

❑ 클로저를 반환하는 함수의 사용

클로저를 함수에 파라미터로 보낼 수 있는 것 처럼, 클로저를 반환(return)하는 함수 또한 선언 할 수 있다.

// 클로저를 반환하는 함수의 선언
func assistFromKane() -> (String, String) -> Void {
    return {
        print("\($0)의 롱패스... \($1)의 골!!!")
    }
}

// 클로저를 반환하는 함수의 호출 방법
let goalFromSon = assistFromKane()
goalFromSon("해리케인", "손흥민")

// output
// 해리케인의 롱패스... 손흥민의 골!!!

 

❑ 클로저 Capture(캡처)

클로저 캡처는 클로저 내부에서 사용되는 함수에서 값을 만드는 경우 생긴다. 아래 코드와 같이 클로저를 여러 번 호출하면 함수 내부에 선언한 goal 변수의 값이 점점 증가하는 것을 볼 수 있다. goal 변수가 함수 내부에 생성되었지만 클로저에 의해 Capture되기 때문에 계속 클로저에도 변수가 살아있게된다. 클로저가 몇 번 호출됐는지 확인하는 용도로도 사용할 수 있다.

클로저 캡처에 대한 자세한 내용을 다룬 블로그를 찾음 : https://velog.io/@kimdo2297/%ED%81%B4%EB%A1%9C%EC%A0%B8-%EC%BA%A1%EC%B3%90%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C-about-closure-capture

func assistFromKane() -> (String, String) -> Void {
    var goal = 0
    return {
        goal += 1
        print("\($0)의 롱패스... \($1)의 골!!!")
        print("골 수 : \(goal)")
    }
}

let goalFromSon = assistFromKane()
goalFromSon("해리케인", "손흥민")
goalFromSon("베르통언", "손흥민")
goalFromSon("모우라", "손흥민")

//output
//해리케인의 롱패스... 손흥민의 골!!!
//골 수 : 1
//베르통언의 롱패스... 손흥민의 골!!!
//골 수 : 2
//모우라의 롱패스... 손흥민의 골!!!
//골 수 : 3

 

반응형

'Apple > Swift' 카테고리의 다른 글

Swift 04 - 클래스(class)  (0) 2022.03.12
Swift 03 - 구조체(struct)  (0) 2022.03.12
Swift 01 - 함수(function)  (0) 2022.03.11
firebase install error/fail  (0) 2020.04.26
Swift > awakeFromNib()  (0) 2020.04.21