📱 Mobile/🚀 Swift

[Swift] 함수 - (3)

exception_log 2021. 1. 25. 19:51

<!— 본 포스트는 "꼼꼼한 재은씨의 Swift : 문법편"을 공부하며 직접 정리한 포스트입니다. —>

 

🚀작성자의 Swift 정리 원본인 Notion 구경하기 -> www.notion.so/Swift-59150070adb0467ea11d4f69090dbb24

 

클로저

🚀 스위프트에서 클로저는 일회용 함수를 작성할 수 있는 구문이다. 일회용 함수란 한 번만 사용한 구문들의 집합이면서, 그 형식은 함수로 작성되어야하는 제약조건이 있을 때 만들어 사용할 수 있는 함수를 이야기한다. 전통적인 명령형 언어에서는 모든 함수에 이름이 부여되어야 하지만 일회용 함수는 한 번만 사용하면 되므로 굳이 함수의 이름을 작성할 필요 없이 생략한다는 점에서 "익명 함수"라고 부르기도 한다.

🚀 스위프트에서 클로저라고 부르는 객체는 대부분 다음 세가지의 경우 중 하나에 해당한다.

  1. 전역 함수 : 이름이 있으며, 주변 환경에서 캡쳐할 어떤 값도 없는 클로저
  1. 중첩 함수 : 이름이 있으며, 자신을 둘러싼 함수로부터 값을 캡처할 수 있는 클로저
  1. 클로저 표현식 : 이름이 없으며 주변 환경으로부터 값을 캡쳐할 수 있는 경량 문법으로 작성된 클로저

클로저 표현식

{ (매개변수) -> 반환 타입 in 
	실행할 구문
}

{ () -> () in 
	print("클로저가 실행됩니다.")
}

반환 값이 없는 경우에는 () 보다는 Void 로 명확히 표현해 주는 것이 좋다.
{ () -> Void in 
	print("클로저가 실행됩니다.")
}

🚀 클로저 실행

// 방법 1
let f = { () -> Void in 
	print("클로저 실행")
}

f() //실행

// 방법 2
({ () -> Void in
	print("클로저가 실행됩니다.")
})() 

🚀 매개변수가 있는 클로저

let c = { (s1: Int, s2: String) -> Void in
	print("s1: \(s1), s2:\(s2)")
}
c(1, "closure")

// 실행 결과 >>
s1: 1, s2: closure

위의 함수를 아래와 같이 쓸 수도 있다.
({ (s1: Int, s2: String) -> Void in
	print("s1:\(s1), s2:\(s2)")
})(1, "closure")

🚀 클로저를 사용하여 문법 자체를 간결하게 작성할 수는 있지만, 이는 가독성이 떨어지는 결과를 가져온다. 따라서 간결성과 가독성의 비율을 항상 고려할 필요가 있다.

클로저 표현식과 경량 문법

클로저 표현식은 주로 인자값으로 사용되는 객체인 만큼, 간결성을 극대화하기 위해 생략할 수 있는 구문들로 이루어져 있다. 필요에 따라 여러 부분을 생략할 수도 있다. 다음의 예시를 살펴보자

// 정렬 기준이 되는 함수를 작성하고 sort(by:)메소드의 인자로 넣어 배열을 정렬하기
func order(s1: Int, s2: Int) -> Bool {
	if s1 > s2 {
		return true
	} else {
		return false
	}
}

value.sort(by: order)

// 클로저 표현식으로 변경
{
	(s1: Int, s2: Int) -> Bool in
	if s1 > s2 {
		return true
	} else {
		return false
	}
}

// 이 클로저 표현식은 sort 메소드의 인자값으로 바로 사용이 가능하다.
value.sort(by: {
	(s1: Int, s2: Int) -> Bool in
	if s1 > s2 {
		return true
	} else {
		return false
	}
})

// 더욱 간결한 형태의 표현식으로 바꾸기
{ (s1: Int, s2: Int) -> Bool in
	return s1 > s2
}

//반환값을 생략한 형태
{ (s1: Int, s2: Int) in
	return s1 > s2
}

// sort 메소드의 인자값으로 사용되었을 때
value.sort(by: { (s1: Int, s2: Int) in return s1 > s2 })

// 매개변수 타입 생략
value.sort(by: { (s1 , s2) in return s1 > s2 })

// 매개변수도 생략하기
value.sort(by: { $0 > $1 })

// sort 메소드에는 클로저 표현식보다 더 간결하게 표현할 수 있는 방법이 있다.
value.sort(by: >)

@escaping과 @autoescape

🚀 @escaping

인자값으로 전달된 클로저를 저장해 두었다가, 나중에 다른 곳에서도 실행할 수 있도록 허용해주는 속성이다.

func callback(fn: () -> Void) {
	fn()
}

callback {
	print("Closure가 실행되었습니다.")
}

정의된 함수 callback(fn:)은 매개변수를 통해 전달된 클로저를 함수 내부에서 실행하는 역할을 한다. 위의 함수를 살짝 수정해보자.

func callback(fn: () -> Void) {
	let f = fn
	f()
}

그런데 수정한 코드를 실행하면 오류가 발생한다. 오류의 내용은 Non-escaping 파라미터인 'fn'은 오직 직접 호출하는 것만 가능하다는 의미이다. 이를 이해하려면 인자값으로 전달되는 클로저의 특성을 알아야 한다.

스위프트에서 함수의 인자값으로 전달된 클로저는 기본적으로 탈출 불가의 성격을 가진다. 해당 클로저를 1. 함수 내에서 2. 직접 실행을 위해서만 사용해야 하는 것을 의미하며, 이 때문에 함수 내부라 할지라도 변수나 상수에 대입할 수 없다. 변수나 상수에 대입을 허용한다면 내부 함수를 통한 캡쳐 기능을 이용하여 클로저가 함수 바깥으로 탈출할 수 있기 때문이다. (탈출 : 함수 내부 범위를 벗어나서 실행되는 것)

하지만 코드를 작성하다 보면 클로저를 변수나 상수에 대입하거나 중첩 함수 내부에서 사용해야 하는 경우도 있다. 이 때 사용되는 것이 @escaping 속성이다.

func callback(fn: @escaping () -> Void) {
	let f = fn .. 클로저를 상수에 대입
	f()
}

callback {
	print("Closure가 실행되었습니다.")
}

🚀 클로저의 기본 속성을 탈출 불가하게 관리함으로써 얻어지는 가장 큰 이점은 컴파일러가 코드를 최적화하는 과정에서의 성능향상이다. 해당 클로저가 탈출할 수 없다는 것은 컴파일러가 더이상 메모리 관리상의 지저분한 일들에 관여할 필요가 없다는 뜻이기 때문이다.

 

🚀 @autoclosure

@autoclosure 속성은 인자값으로 전달된 일반 구문이나 함수 등을 클로저로 래핑하는 역할을 한다. 쉽게 말해 이 속성이 붙어 있을 경우, 일반 구문을 인자값으로 넣더라도 컴파일러가 알아서 클로저로 만들어서 사용한다는 것이다. 이 속성을 사용하면 인자값을 { } 형태가 아니라 ( )형태로 사용할 수 있다는 장점이 있다. 인자값을 직접 클로저 형식으로 넣어줄 필요가 없기 때문이다.

func condition(stmt: () -> Bool) {
	if stmt() == true {
		print("결과 참")
	} else {
		print("결과 거짓")
	}
}

// 실행 방법 1 - 일반 구문
condition(stmt: {
	4 > 2
})

// 실행 방법 2 - 클로저 구문
condition {
	4 > 2
}

func condition(stmt: @autoclosure () -> Bool) {
	if stmt() == true {
		print("결과 참")
	} else {
		print("결과 거짓")
	}
}

// 실행 방법
condition(stmt: (4 > 2))

🚀 클로저가 아니라 그 안에 들어가는 내용만 인자값으로 넣어줄 뿐이다. 이렇게 전달된 인자값은 컴파일러가 자동적으로 클로저 형태로 감싸 처리해주게 된다. 또한 인자값은 코드에 작성된 시점이 아니라 해당 클로저가 실행되는 시점에 맞추어 실행된다. 이를 지연된 실행이라고 부르며, @autoclosure 속성이 가지는 주요한 특징 중 하나이다.

 

반응형

'📱 Mobile > 🚀 Swift' 카테고리의 다른 글

[Swift] 구조체와 클래스 - (2)  (0) 2021.01.27
[Swift] 구조체와 클래스 - (1)  (0) 2021.01.26
[Swift] 함수 - (2)  (0) 2021.01.25
[Swift] 함수 - (1)  (0) 2021.01.22
[Swift] 옵셔널 (Optional)  (0) 2021.01.21