Swifter {Swift Developer}

메뉴

폐쇄(클로저, Closure)

폐쇄(Closure) 사용방법을 알아보자. 앱을 만드는데 있어서 필수적인 요소이기 때문에 필히 기억하자.

이번 장의 목표

  • 폐쇄를 생성하고 사용하기
  • 열거형을 생성하고 사용하기
  • 문자열 조작하기

 

(1) 폐쇄(Closure)

폐쇄(클로저)는 한마디로 이름없는 함수를 말한다. C#이나 Java에서 이야기하던 람다식이 이에 해당한다.

 

{(인수목록) -> 리턴값 자료형 in return 리턴값}

 

위 형식은 인수와 리턴값이 있는 폐쇄를 정의한 것으로 Int형 인수를 2개 가지고 Int형 리턴값을 하나 리턴하는 폐쇄이다. 이는 폐쇄내에서 받은 2개의 인수를 더해서 리턴값으로 리턴한다. 좀더 이해를 돕기 위해 간단한 예제를 Playground에서 실행해 보자.

 

var sumNum = {(a:Int, b:Int) -> Int in

return a + b

}

 

sumNum(5,7) //12

 

위 폐쇄는 변수 sumNum에 직접 할당하기 때문에 일반적인 함수처럼 sumNum(5, 7)처럼 사용했다.

 

(2) 폐쇄형

폐쇄도 함수와 비슷한 구조를 가지고 있다. 좀더 이해를 돕기 위해 폐쇄를 변수에 할당할 때 형을 지정하는 방법을 살펴보겠다.

 

var sumClouser: (Int, Int) -> Int = {(a:Int, b:Int) -> Int in

return a + b

}

 

sumClouser(6, 8) // 14

 

앞에서 덧셈으로 예를 들었던 것을 약간 변형한 것으로 변수 sumClouser에 폐쇄형을 지정한 것이다. 이 폐쇄는 Int형 인수를 두개 취해서 리턴값으로 Int형 보낸다.

 

또한, 인수도 리턴값도 없는 폐쇄형도 가능하다. 간단하게 Hello World를 출력하는 폐쇄형을 만들어보겠다. 참고로 인수와 리턴값도 없는 폐쇄형은 “() -> ()”으로 구성한다.

var hello:() -> () = {() -> () in

print(“Hello World”)

}

hello()

 

참고로 페쇄의 in에 오는 명령문이 하나밖에 없다면 return문은 생략이 가능하다.

 

(3) 폐쇄 함수 리턴하기

폐쇄는 함수 리턴값으로 사용이 가능하다. 덧셈하는 폐쇄를 리턴하는 함수 sumFunc를 정의해 보자. 함수 sumFunc는 리턴값으로 폐쇄를 리턴하기 떄문에 리턴값 형은 폐쇄형으로 지정해야 한다.

 

func sumFunc() -> (Int, Int) -> Int {

return { a, b -> Int in a + b }

}

 

var sum = sumFunc()

 

sum(5, 9) // 14

 

 

덧셈 폐쇄는 2개의 인수를 받아 덧셈결과(Int)를 리턴하는 함수 sumFunc의 리턴형 (int, Int) -> Int로 처리한다. 즉 변수에 할당된 폐쇄는 일반적인 함수처럼 실행이 가능하다.

 

(4) 함수의 인수 및 폐쇄

폐쇄를 함수의 인수로 사용할 때가 있다.

 

func sumCalc(a:Int, b:Int, sumClosure:(Int,Int) -> Int) {

print(sumClosure(a, b))

}

 

var plus = {(a:Int, b:Int) -> Int in

return a + b

}

 

var multi = {(x:Int, y:Int) -> Int in

return x * y

}

 

sumCalc(5, b:7, sumClosure: plus) //12

 

sumCalc(7, b:9, sumClosure: multi) //63

 

 

sumCalc이라는 함수와 덧셈을 하는 plus폐쇄, 곱셉을 하는 multi를 정의한다. sumCalc함수는 Int형 인수를 2개 폐쇄형 (int, Int) -> Int인수를 하나 받는 구조로 두가지 폐쇄가 있다.

 

(5) 캡쳐

함수 내부에 작성된 폐쇄 또는 함수는 폐쇄 외부에서 선언된 변수를 사용할 수 있는데 이것을 캡쳐라고 한다. 캡쳐된 변수의 값은 함수가 끝난후에도 유지된다.

capture

 

func incrNum(initVal:Int) -> (Int) -> Int {

var total = initVal

 

return { (count:Int) -> Int in

total += count //캡쳐한 total에 count를 추가

return total

}

}

 

var num = incrNum(5) // 5

num(10) // 5 + 10 = 15

num(15) // 5 + 10 + 15 = 30

num = incrNum(20)  // 20

 

위 예제는 incrNum는 수치 증가 함수에서 인수 initVal은 증가하는 초기값이다. 함수내부에서 total라는 변수가 선언되어 있다 (이게 변수A에 해당된다)

incrNum에 인수5를 전달하면 함수 total이 5가 할당되고 폐쇄의 total도 5가 되고 함수 호출은 이 상태의 폐쇄가 리턴된다. 즉 num에 대입되는 것은 total5가 유지되고 폐쇄된다. 다음으로 앞에서 클로저를 대입한 num을 사용하고 있고 num대입된 클로저는 정수인수를 하나주고 여거서 인수에 10을 전달하고 실행한다. 폐쇄 total값은  앞에서 5이었기 떄문에 이에 인수 10이 더해져 15가 된다. 다음 세번째로 같은 구조로 계산되어 30이 된다. 네번쨰로 incrNum함수를 가져간다. Total값이 새로 설정되기 때문에 total값은 인수로 전달된 20이다. 이렇게 캡쳐를 사용하면 값을 유지하는 함수를 만들 수 있다.

 

(6) 후행 폐쇄

함수 인수에는 폐쇄로 전달되는 구조를 앞에서 설명했다. 그러나 인수로 많은 작업을 할 경우 길어진 폐쇄를 설명하고 함수 의 직관성도 떨어진다. 이를 피하기 위해 인수로 전달 폐쇄 부분을 함수 외부에 작성할 수 있다. 이런 방법을 후행 폐쇄라고 한다. 트레일링 클로저라고 말하며 함수 마지막 인수가 폐쇄인 경우이다.

 

func calc(a:Int, b:Int, calcClosure: (Int, Int) -> Int) -> Int {

return calcClosure(a, b)

}

 

calc(5, b:3, calcClosure: {(x:Int, y:Int) -> Int in return x + y})

 

calc(5, b:3) {

(x:Int, y:Int) -> Int in

return x + y  //8

}

 

함수 calc은 인수가 3개가 있고  Int형 a, b와 폐쇄 (Int, Int) -> Int로 구성되어 있다. 이 함수 내부에서는 인자로 받은 폐쇄에 a, b를 전달하고 실행하고 그 결과를 리턴한다. 다만 함수 calc의 3번째 인수에 폐쇄를 전달하지만 어디부터 어디까지가 폐쇄인지를 바로 파악하기 어렵다.  후행폐쇄로 처리하기 위해 a, b 인수 다음 {}을 붙여 폐쇄인 것을 알 수 있게 한다.

 

(7) 폐쇄를 사용한 배열정렬

sort라는 컬렉션정렬방법이 이다. Sort메소드는 아래와 같이 사용한다.

컬렉션.sort(폐쇄)

 

var num = [7, 2, 3, 5, 9]

 

var sortCheck = {(a:Int, b:Int) -> Bool in return a > b}

var descValue = num.sort(sortCheck)

 

sortCheck = {(a:Int, b:Int) -> Bool in return a < b}

var ascValue = num.sort(sortCheck)

 

배열을 준비하고 내림차순에 정렬폐쇄로 배열데이터를 정렬하려면 두 요소를 제거하고 비교시 어느쪽이 앞에 오는지를 결정해야 한다. 즉 두개를 인수를 받아 첫번째 인수a가 다음 인수 b보다 큰 경우 true라는 조건식를 리턴하고 sort메소드에 배열데이터를 정렬후 descValue에 대입한다.

반대로 배열을 오름차순하는 정렬폐쇄로 a는 b보다 작은 경우 true를 리턴하고 sort메소드를 사용하여 오름차순정렬후 ascValue에 대입한다.

 

(8) 폐쇄를 사용한 배열데이터 조작

map이라는 컬렉션이 가지는 모든 요소를 조작하는 방법이 있다. Map메소드를 사용하면 배열의 모든 요소를 조작이 가능하다.

컬렉션.map(폐쇄)

 

var num = [7, 2, 3, 5, 9]

 

var cnt = {(a:Int) -> Int in return a * 100}

 

var data1 = num.map(cnt)

 

참고로 폐쇄를 단축하는 방법은 폐쇄인수나 키워드를 생략가능하다.

[타이틀이미지출처: treehouse]

 

Facebook Comments

카테고리:   Swift

댓글

죄송하지만 댓글은 닫혀 있습니다.