swift 기초 문법 - Closure(클로저) 파헤치기

Closure(클로저)는 일종의 실행 가능한 코드블럭이라고 할 수 있습니다. 이름 없는 함수(메소드)라고 합니다. 이전에 공부 한 함수는 Closure의 형태로 이름이 있는 클로저라 할 수 있습니다. C++에서의 람다와 비슷하다고 이해했습니다.

클로저는 전달 인자로도 넘겨줄 수 있으며, 변수나 상수로 저장될 수 도 있습니다. 그리고 함수의 반환 값으로도 사용할 수 있죠. 그리고 클로저는 레퍼런스 타입입니다.

 

Closure

//Closure
{ (parameters) -> return type in 
	//... code
}

var addClosure: (Int, Int) -> Int = { (a: Int, b: Int) -> Int in 
	return a + b
}

클로저 표현 방식입니다. 함수와 비슷한 구조이며 func 키워드가 없고 in이 있는 차이입니다. in 이전의 내용은 인자, 반환 값의 정의이며 그 이후는 코드가 클로저의 로직 시작입니다. 클로저는 코드 블럭이기 때문에 함수처럼 호출할 수 있는 이름이 존재하지 않습니다. 하지만 addClosure처럼 변수의 타입으로 클로저 선언이 가능하며 저장할 수 도있습니다.

 

축약하기

//Closure
var addClosure: (Int, Int) -> Int = { (a, b) in return a + b }
var addClosure: (Int, Int) -> Int = { (a, b) in a + b }
var addClosure: (Int, Int) -> Int = { $0 + $1 } //인자의 순서가 번호로 매겨집니다.

Closure의 타입을 생략할 수도 있습니다. 타입이 명시되어있을 경우에는 이렇게 생략하여서 만들어 줄 수 있습니다. return역시 생략이 가능합니다(클로저 로직이 한 줄 일 경우). 인자의 이름까지도 생략할 수 있습니다. 표현 방식은 $을 통해서 할 수 있고, 숫자로 인자 위치를 찾을 수 있습니다. 이것을 단축 인자 이름이라고 합니다.

 

사용하기

//두 수의 덧셈함수
func operateTwoNumber(a: Int, b: Int, operation: (Int, Int) -> Int) -> Int {
	return operation(a, b)
}

인자로 받은 클로저를 사용하여 두 수를 계산 후 반환하는 함수입니다. 클로저를 이용하게 되면 하나의 함수로 다양한 계산을 할 수 있습니다. 물론 클로저의 계산 로직이 필요합니다.

//두 수의 덧셈함수
var addClosure: (Int, Int) -> Int = { (a, b) in a + b }
var subClosure: (Int, Int) -> Int = { (a, b) in a - b }
operateTwoNumber(a: 10, b: 5, operation: addClosure)
//결과 : 15
operateTwoNumber(a: 10, b: 5, operation: subClosure)
//결과 : 5

앞에서 만든 operateTwoNumberaddClosure를 이용하면 덧셈을 할 수가 있습니다. 추가로 subClosure를 만들어 주었습니다. operation의 값으로 전달해주면 우리는 뺄셈을 할 수가 있겠네요.

 

후행 클로저

let result = operateTwoNumber(a: 10, b: 2){a, b in a / b}

함수의 마지막 전달 인자가 클로저일 때 후행 클로저를 사용할 수 있습니다. Xcode에서도 자동 완성을 후행 클로저를 우선시해줍니다. 후행 클로저는 클로저가 길어서 한 줄에 작성할 수 없을 때 가장 유용합니다.

 

클로저 반환하기

func greeting(name: String) -> String{
	return "hi, " + name
}
greeting(name: "minmong")

//return type {() -> String} Closure
func greetingClosure(name: String) -> () -> String{
	return { () in return "hi, " + name}
}
greetingClosure(name: "minmong")()
//결과 : "hi, minmong"

클로저를 반환하는 함수입니다. 굳이 이렇게 만들어야 할 내용은 아닙니다. 반환하는 클로저 모습을 익히기 위한 것입니다. greeting함수는 name을 받아 문자열을 반환해주는 함수입니다. 이것을 클로저를 이용해서 표현해봅시다. greetingClosure는 반환 타입이 () -> String인 클로저입니다. 함수 내부의 반환 형태도 클로저 형태가 되어야 합니다. (앞에서 처럼 in, return 생략이 가능합니다.) 그런데 마지막 라인 코드 모양이 좀 특이합니다. 함수 호출 후 ()가 한 번 더 사용이 되었는데요. 이건 반환 값이 클로저자체기 때문에 클로저 호출을 통해서 내부 로직을 실행시키는 모습입니다.

 

func greetingClosure(name: String) -> (String) -> String{
    return { greet in return "hi, " + greet + " " + name}
}
greetingClosure(name: "minmong")("hello")
//결과 : "hi, hello minomng"

이렇게 클로저 인자가 있을 경우 값을 전달해 주어야 합니다.

 

Capturing Values (캡처)

클로저는 스코프의 상수와 변수를 캡처할 수가 있습니다. C++ 람다에서도 캡처가 있었는데요. 그거와 유사한 듯합니다. 캡처의 뜻은 쉽게 생각하면 순간을 기억 저장하는 것이죠? 코드에서는 블럭 단위로 나뉘게 됩니다. 예를 들어 함수에서 할당 한 로컬 변수는 함수 외부에서 사용을 할 수가 없게 없습니다. 일반적으로 함수를 사용하고 나면 해당 로컬 변수의 메모리(스택)는 소멸되기 때문입니다. 클로저는 클로저 외부의 상수와 변수를 캡처할 수 있습니다. 외부의 상수와 변수를 사용하기 위해서 참조한다고 생각하면 됩니다.

func capturingClosure(addingValue: Int) -> () -> Int {
    var totalValue = 0
    return {
        totalValue += addingValue
        return totalValue
    }
}

let addFive = capturingClosure(capturingValue: 5)

addFive() //결과 : 5
addFive() //결과 : 10
addFive()	//결과 : 15

captureingClosure함수 입니다. 반환 타입은 () -> Int입니다. 함수는 addingValue인자와 totalValue로컬 변수가 있습니다. 반환되는 클로저에서는 totalValue에 addingValue를 더하고 있습니다. capturingClosure함수를 호출해봅시다. 값으로는 5를 전달하고 있습니다. addFive에 클로저를 할당해 줍니다. 여기서 실제 함수 호출은 한 번 일어났습니다.

 

하지만 addFive를 호출하게 되면 addFive에 참조로 할 당 됩니다. 클로저 내부에서 함수의 로컬 변수들이 캡처되어 5, 10, 15로 증가됨을 볼 수 있습니다. 함수 호출 후 소멸해야 할 변수 메모리가 사라지지 않은 것이죠. 캡처된 변수는 클로저가 실행될 때에 정해진다고 합니다. 만약 캡처를 생성시점으로 하기 위해서는 별도의Capture List를 사용해야 합니다. 이 부분은 추후에 작성해보겠습니다.

 

 

그리드형(광고전용)

이 글을 공유합시다

facebook twitter googleplus kakaoTalk kakaostory naver band

댓글

Designed by JB FACTORY