About Closure
오늘은 클로저에 대해 알아보도록 하자.
미리보기
- 클로저 형태
- 왜 클로저를 사용하지?
- 여러가지 문법
아래는 나중에 다룰 예정클로저의 메모리구조@escaping, @autoclosure 키워드캡처현상, 캡처리스트사용 예시
우선 클로저란 무엇일까?
클로저 (Closures) | Swift
명명된 함수 생성없이 실행되는 코드 그룹입니다. 클로저 (Closures) 는 코드에서 주변에 전달과 사용할 수 있는 자체 포함된 기능 블럭입니다. Swift의 클로저는 다른 프로그래밍 언어에서 클로저,
bbiguduk.gitbook.io
- 해당 글에 따르면 클로저는 ‘코드에서 주변에 전달과 사용할 수 있는 자체 포함된 기능 블럭’이다.
본격적으로 들어가기 전에 아래와 같은 사실을 기억해두자
“스위프트는 함수를 ‘일급객체’로 취급한다”
???
일급 객체 - 위키백과, 우리 모두의 백과사전
위키백과, 우리 모두의 백과사전. 컴퓨터 프로그래밍 언어 디자인에서, 일급 객체(영어: first-class object)란 다른 객체들에 일반적으로 적용 가능한 연산을 모두 지원하는 객체를 가리킨다. 보통
ko.wikipedia.org
- 따라서 다음과 같은 내용이 성립한다.
- 함수는 “타입”이다.
- 함수를 변수에 할당할 수 있다.
- 함수를 호출할 때, “함수”를 파라미터로 전달할 수 있음
- 함수에서 “함수”를 반환할 수 있다.
- 함수는 “타입”이다.
- 클로저와 함수는 기능은 완전히 동일한데, 형태만 다름.
그렇다면 기존 함수와 클로저는 무엇이 다른걸까?
- 우선 아래의 코드를 통해 형태가 어떻게 다른 지 살펴보자.
// 일반적으로 사용했던 함수
func aFunction(str: String) -> String {
return "Hello, \(str)"
}
// 클로저의 형태 (현재는 변수에 담아서 사용)
let a = {(str: String) -> String in
return "Hello, \(str)"
}
// 타입 추론이 가능한 클로저의 형태
let _: (Int, Int) -> Int = {(a, b) in
let result = a + b
return result
}
// 주로 사용하는 클로저의 형태
let _: { (파라미터) in
let result = a + b
return result
}
처음 봤을 때, 어떻게 저렇게 간단해지지라는 생각을 했다.
이를 좀 더 자세히 단계를 세분화해서 보자면
func add(a: Int, b: Int) -> Int {
let result = a + b
return result
}
// 1단계 이름 없애기
// (a: Int, b: Int) -> Int **{**
// let result = a + b
// return result
// }
// 2단계 괄호 위치 변경
{ (a: Int, b: Int) -> Int
let result = a + b
return result
}
// 3단계 문법
{ (a: Int, b: Int) -> Int in
let result = a + b
return result
}
// 4단계 타입 생략
{ (a: Int, b: Int) in
let result = a + b
return result
}
// 5단계 타입 생략
{ (a, b) in
let result = a + b
return result
}
- 위와 같은 특징을 보자면..
- 리턴형에 대한 표기를 생략 할 수 있고
- 파라미터의 타입의 생략도 대부분 가능하다.
- 단, 컴파일러가 타입 추론 가능한 경우에 해당된다.
클로저를 사용하는 이유
- 클로저와 일반적으로 사용하는 함수에 대한 ‘형태적 차이’는 확인을 할 수 있는데..
- 그렇다면 왜 굳이 다른 형태로 클로저라는 것을 사용하는 걸까?
- 왜 이름이 필요 없을까?
- 함수를 실행할 때 전달하는 형태로 사용하기 때문에 이름이 필요없기 때문.
[아래의 코드 예시를 보자]
func usingClosureFunction(closure: () -> ()) {
print("클로저 준비...!")
closure()
}
usingClosureFunction(closure: {
print("클로저 끝...!")
})
// "클로저 준비...!"
// "클로저 끝...!"
위의 상황이면 그냥 함수를 사용하면 되는 거 아닌가?
[아래의 예시를 보자]
func usingClosureFunction2(a: Int, b: Int, closure: (Int) -> Void) {
let c = a + b
closure(c)
}
usingClosureFunction2(a: 2, b: 3, closure: { c in
print("더하기 결과 : \(c)")
print("더한 뒤 제곱 : \(c*c)")
... // 자유롭게 설정 가능(Type에 맞춰)
}
- 위와 같이 개발자가 향후, 원하는대로 정의할 수 있기 때문에 활용도가 늘어난다.
클로저의 문법 최적화
- 개발자는 쉽고, 간결하게 하기 위해 애쓰는 것 같다…
- 아래와 같이 실제 사용시 읽기 쉽고 간결한 코드 작성을 위해 축약된 형태의 문법을 제공한다.
- 문맥상에서 파라미터와 리턴밸류 타입 추론(Type Inference)
- 싱글 익스프레션인 경우(한줄), 리턴을 안 적어도 됨(Implicit Return)
- 아규먼트 이름을 축약(Shorthand Arguments) >> $0, $1
- 트레일링 클로저 문법 : 함수의 마지막 전달 인자(아규먼트)로 클로저 전달되는 경우, 소괄호를 생략 가능
하나씩 살펴보자.
- 후행(Trailing) 클로저
func usingtrailing(closure: () -> Void) {
print("ready..")
closure()
}
// 아래 형태가 함수를 실행하고 마지막 아규먼트로 클로저를 전달한 형태라는 거에 익숙해져야
usingtrailing {
print("프린트 종료")
}
func anothertrailing(a: Int, b: Int, closure: (Int) -> Void) {
let c = a + b
closure(c)
}
anothertrailing(a: 3, b: 4) { (num) -> Void in
print("num")
}
- 파라미터 및 생략 등의 간소화
{ (num) in
return num % 2 == 0)
}
// after 간소화...
{ $0 % 2 == 0 }
func performClosure(param: (String) -> Int) {
param("Swift")
}
// 문법을 최적화하는 과정
// 1) 타입 추론(Type Inference) "-> Int" 생략
performClosure(param: { (str: String) in
return str.count
})
performClosure(param: { str in
return str.count
})
// 아규먼트 이름을 축약 (Shorthand Arguments)
performClosure(param: {
$0.count
})
// 트레일링 클로저
performClosure(param: {
$0.count
})
performClosure() {
$0.count
}
// Result
performClosure { $0.count }
- 사용 예시
// Timer.scheduledTimer을 치고 엔터를 누르면 아래와 같은 형태로 나와서 입력
Timer.scheduledTimer(withTimeInterval: 0.2, repeats: false, block: <#T##(Timer) -> Void#>)
// 이를 다르게 보면
Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { (timer) in
print("0.5초뒤에 출력하기")
}
- 멀티플 후행(Trailing) 클로저
- 여러개의 클로저(함수)가 파라미터로 사용된 함수 실행 시, 아규먼트로 구분짓고, 여러개의 클로저로 연결 가능
func multiple(first: () -> (), second: () -> (), third: () -> () {
first()
second()
third()
}
// 사용시
multiple {
print("1")
} second: {
print("2")
} third: {
print("3")
}
-> 클로저의 메모리 구조, 캡처리스트와 같은 항목은 양이 상당하게 다음번에 다룰 예정이다.
'iOS > Swift' 카테고리의 다른 글
About Class, Struct (1) | 2024.03.19 |
---|---|
About Delegate Pattern (0) | 2024.03.10 |
[Swift] Date, Calendar, DateFormatter (1) | 2024.02.25 |
Result Type에 대한 이해 (1) | 2024.02.19 |
제네릭(Generics) (0) | 2024.02.18 |
About Closure
오늘은 클로저에 대해 알아보도록 하자.
미리보기
- 클로저 형태
- 왜 클로저를 사용하지?
- 여러가지 문법
아래는 나중에 다룰 예정클로저의 메모리구조@escaping, @autoclosure 키워드캡처현상, 캡처리스트사용 예시
우선 클로저란 무엇일까?
클로저 (Closures) | Swift
명명된 함수 생성없이 실행되는 코드 그룹입니다. 클로저 (Closures) 는 코드에서 주변에 전달과 사용할 수 있는 자체 포함된 기능 블럭입니다. Swift의 클로저는 다른 프로그래밍 언어에서 클로저,
bbiguduk.gitbook.io
- 해당 글에 따르면 클로저는 ‘코드에서 주변에 전달과 사용할 수 있는 자체 포함된 기능 블럭’이다.
본격적으로 들어가기 전에 아래와 같은 사실을 기억해두자
“스위프트는 함수를 ‘일급객체’로 취급한다”
???
일급 객체 - 위키백과, 우리 모두의 백과사전
위키백과, 우리 모두의 백과사전. 컴퓨터 프로그래밍 언어 디자인에서, 일급 객체(영어: first-class object)란 다른 객체들에 일반적으로 적용 가능한 연산을 모두 지원하는 객체를 가리킨다. 보통
ko.wikipedia.org
- 따라서 다음과 같은 내용이 성립한다.
- 함수는 “타입”이다.
- 함수를 변수에 할당할 수 있다.
- 함수를 호출할 때, “함수”를 파라미터로 전달할 수 있음
- 함수에서 “함수”를 반환할 수 있다.
- 함수는 “타입”이다.
- 클로저와 함수는 기능은 완전히 동일한데, 형태만 다름.
그렇다면 기존 함수와 클로저는 무엇이 다른걸까?
- 우선 아래의 코드를 통해 형태가 어떻게 다른 지 살펴보자.
// 일반적으로 사용했던 함수
func aFunction(str: String) -> String {
return "Hello, \(str)"
}
// 클로저의 형태 (현재는 변수에 담아서 사용)
let a = {(str: String) -> String in
return "Hello, \(str)"
}
// 타입 추론이 가능한 클로저의 형태
let _: (Int, Int) -> Int = {(a, b) in
let result = a + b
return result
}
// 주로 사용하는 클로저의 형태
let _: { (파라미터) in
let result = a + b
return result
}
처음 봤을 때, 어떻게 저렇게 간단해지지라는 생각을 했다.
이를 좀 더 자세히 단계를 세분화해서 보자면
func add(a: Int, b: Int) -> Int {
let result = a + b
return result
}
// 1단계 이름 없애기
// (a: Int, b: Int) -> Int **{**
// let result = a + b
// return result
// }
// 2단계 괄호 위치 변경
{ (a: Int, b: Int) -> Int
let result = a + b
return result
}
// 3단계 문법
{ (a: Int, b: Int) -> Int in
let result = a + b
return result
}
// 4단계 타입 생략
{ (a: Int, b: Int) in
let result = a + b
return result
}
// 5단계 타입 생략
{ (a, b) in
let result = a + b
return result
}
- 위와 같은 특징을 보자면..
- 리턴형에 대한 표기를 생략 할 수 있고
- 파라미터의 타입의 생략도 대부분 가능하다.
- 단, 컴파일러가 타입 추론 가능한 경우에 해당된다.
클로저를 사용하는 이유
- 클로저와 일반적으로 사용하는 함수에 대한 ‘형태적 차이’는 확인을 할 수 있는데..
- 그렇다면 왜 굳이 다른 형태로 클로저라는 것을 사용하는 걸까?
- 왜 이름이 필요 없을까?
- 함수를 실행할 때 전달하는 형태로 사용하기 때문에 이름이 필요없기 때문.
[아래의 코드 예시를 보자]
func usingClosureFunction(closure: () -> ()) {
print("클로저 준비...!")
closure()
}
usingClosureFunction(closure: {
print("클로저 끝...!")
})
// "클로저 준비...!"
// "클로저 끝...!"
위의 상황이면 그냥 함수를 사용하면 되는 거 아닌가?
[아래의 예시를 보자]
func usingClosureFunction2(a: Int, b: Int, closure: (Int) -> Void) {
let c = a + b
closure(c)
}
usingClosureFunction2(a: 2, b: 3, closure: { c in
print("더하기 결과 : \(c)")
print("더한 뒤 제곱 : \(c*c)")
... // 자유롭게 설정 가능(Type에 맞춰)
}
- 위와 같이 개발자가 향후, 원하는대로 정의할 수 있기 때문에 활용도가 늘어난다.
클로저의 문법 최적화
- 개발자는 쉽고, 간결하게 하기 위해 애쓰는 것 같다…
- 아래와 같이 실제 사용시 읽기 쉽고 간결한 코드 작성을 위해 축약된 형태의 문법을 제공한다.
- 문맥상에서 파라미터와 리턴밸류 타입 추론(Type Inference)
- 싱글 익스프레션인 경우(한줄), 리턴을 안 적어도 됨(Implicit Return)
- 아규먼트 이름을 축약(Shorthand Arguments) >> $0, $1
- 트레일링 클로저 문법 : 함수의 마지막 전달 인자(아규먼트)로 클로저 전달되는 경우, 소괄호를 생략 가능
하나씩 살펴보자.
- 후행(Trailing) 클로저
func usingtrailing(closure: () -> Void) {
print("ready..")
closure()
}
// 아래 형태가 함수를 실행하고 마지막 아규먼트로 클로저를 전달한 형태라는 거에 익숙해져야
usingtrailing {
print("프린트 종료")
}
func anothertrailing(a: Int, b: Int, closure: (Int) -> Void) {
let c = a + b
closure(c)
}
anothertrailing(a: 3, b: 4) { (num) -> Void in
print("num")
}
- 파라미터 및 생략 등의 간소화
{ (num) in
return num % 2 == 0)
}
// after 간소화...
{ $0 % 2 == 0 }
func performClosure(param: (String) -> Int) {
param("Swift")
}
// 문법을 최적화하는 과정
// 1) 타입 추론(Type Inference) "-> Int" 생략
performClosure(param: { (str: String) in
return str.count
})
performClosure(param: { str in
return str.count
})
// 아규먼트 이름을 축약 (Shorthand Arguments)
performClosure(param: {
$0.count
})
// 트레일링 클로저
performClosure(param: {
$0.count
})
performClosure() {
$0.count
}
// Result
performClosure { $0.count }
- 사용 예시
// Timer.scheduledTimer을 치고 엔터를 누르면 아래와 같은 형태로 나와서 입력
Timer.scheduledTimer(withTimeInterval: 0.2, repeats: false, block: <#T##(Timer) -> Void#>)
// 이를 다르게 보면
Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { (timer) in
print("0.5초뒤에 출력하기")
}
- 멀티플 후행(Trailing) 클로저
- 여러개의 클로저(함수)가 파라미터로 사용된 함수 실행 시, 아규먼트로 구분짓고, 여러개의 클로저로 연결 가능
func multiple(first: () -> (), second: () -> (), third: () -> () {
first()
second()
third()
}
// 사용시
multiple {
print("1")
} second: {
print("2")
} third: {
print("3")
}
-> 클로저의 메모리 구조, 캡처리스트와 같은 항목은 양이 상당하게 다음번에 다룰 예정이다.
'iOS > Swift' 카테고리의 다른 글
About Class, Struct (1) | 2024.03.19 |
---|---|
About Delegate Pattern (0) | 2024.03.10 |
[Swift] Date, Calendar, DateFormatter (1) | 2024.02.25 |
Result Type에 대한 이해 (1) | 2024.02.19 |
제네릭(Generics) (0) | 2024.02.18 |