[Swift 문법] 7. 클로저 (Closure)

JJOOEE__·2024년 6월 17일
2

Swift 문법

목록 보기
13/13
post-thumbnail

🍎 7. 클로저 (Closure)

🍏 1. 클로저의 종류

◻️Named Closure

  • 이름이 있는 함수
  • func 키워드를 사용해서 명명해준 함수들
  • 이를 클로저라 부르지 않고,
    그냥 함수라고 부르는 것 뿐임! (그치만 클로저란 사실은 변함 없음)

◻️Unnamed Closure

함수와 클로저 비교

특징함수 (Function)클로저 (Closure)
선언 방법func 키워드를 사용하여 선언{ }를 사용하여 선언
이름 유무이름이 반드시 있어야 함이름이 없을 수도 있음 (익명 클로저)
캡처링함수 내에서는 외부 변수를 캡처하지 않음외부 변수를 캡처할 수 있음
반복 사용정의된 함수 이름으로 여러 곳에서 호출 가능필요 시에 바로 사용할 수 있음 (인라인 사용)
함수 타입func로 명시된 타입클로저 타입 (인자 타입) -> (반환 타입)
인라인 사용인라인 사용 불가능인라인 사용 가능
Self 참조클래스 메서드 내에서 self 필요캡처리스트를 통해 self 참조 가능
축약된 형태 제공제공되지 않음축약된 형태 제공 (예: 매개변수 및 반환 생략)

🍏 2. 클로저의 표현식

  • 클로저는 익명함수로 func를 사용하지 않음

◻️ 클로저 헤드와 클로저 바디

  • Closure Head
  • Closure Body
  • 이 둘을 구분지어 주는게 in키워드!

◻️ 2-1. Parameter와 Return Type이 둘 다 없는 클로저

let closure = { () -> () in
    print("Closure")
}

◻️ 2-2. Parameter와 Return Type이 있는 클로저

let closure = { (name: String) -> String in
    return "Hello, \(name)"
}
  • 클로저에선 Argument Label을 사용하지 않음
    • name = Parameter Name != Argument Label
closure("jjoohee")
closure(name: "jjoohee")  //error!
  • 클로저를 호출할 땐 Argument Label을 사용하지 않음! 사용하면 에러!!!!!

🍏 3. 클로저 실행하기

◻️ 3-1. 클로저가 대입된 변수나 상수로 호출하기

  • 이런 식으로 클로저가 대입된 상수 closure를 호출 구문인 ()를 이용해서 실행시킬 수 있음

let closure = { () -> String in
    return "Hello jjoohee!"
}

closure()

◻️ 3-2. 클로저를 직접 실행하기

  • 클로저를 변수나 상수에 대입하지 않고 실행시키고 싶다면, (완벽한 일회성)
    그땐 클로저를 ( ) 소괄호로 감싸고, 마지막에 호출 구문인 ()를 추가해주면 됨!
({ () -> () in
    print("Hello jjoohee!")
})()

🍏 4. 1급 객체로서 클로저

◻️ ⓛ 클로저를 변수나 상수에 대입할 수 있다

let closure = { () -> () in
    print("Closure")
}

기존에 클로저를 대입한 변수나 상수 > 새로운 변수나 상수에 대입할 수도 있음

let closure2 = closure

② 함수의 파라미터 타입으로 클로저를 전달할 수 있다

func doSomething(closure: () -> ()) {
    closure()
}
doSomething(closure: { () -> () in
    print("Hello!")
})
  • 함수를 파라미터로 전달받는 doSomething이라는 함수가 있음
    이 경우, 파라미터로 함수를 넘겨줘도 되지만 클로저를 넘겨줘도 됨!!

◻️ ③ 함수의 반환 타입으로 클로저를 사용할 수 있다

func doSomething() -> () -> () {
}
func doSomething() -> () -> () {
    
    return { () -> () in
        print("Hello jjoohee!")
    }
}
let closure = doSomething()
closure()

를 출력하면 Hello jjoohee! 가 나오는것!

🍎 클로저 (Closure) 경량화

🍏 1. 트레일링 클로저(Trailing Closure)

  • 클로저를 좀 더 보기 편하게 문법을 변형하는 것

◻️ 트레일링 클로저란?

  • 함수의 마지막 파라미터가 클로저일 때,
    이를 파라미터 값 형식이 아닌 함수 뒤에 붙여 작성하는 문법
    이때, Argument Label은 생략된다

  • 중요한 것! 마지막 파라미터가 클로저, Argument Label은 생략!!

◻️ 1-1. 파라미터가 클로저 하나인 함수

  • 클로저 하나만 파라미터로 받는 함수
func doSomething(closure: () -> ()) {
    closure()
}

함수 호출 방법

doSomething(closure: { () -> () in
    print("Hello!")
})
  • 클로저가 파라미터의 값 형식으로 함수 호출 구문 ()안에 작성되어 있는 것 = Inline Closure

🌈 ① 파라미터가 클로저 하나일 경우, 이 클로저는 첫 파라미터이자 마지막 파라미터이므로 트레일링 클로저가 가능

🌈 ② "closure"라는 ArgumentLabel은 트레일링 클로저에선 생략됨

◻️ 1-2. 파라미터가 여러 개인 함수

  • 첫번째 파라미터 success 클로저, 두번째 파라미터 fail 클로저를 받는 함수 fetchData
func fetchData(success: () -> (), fail: () -> ()) {
    //do something...
}
  • 함수 fetchData 호출

fetchData(success: { () -> () in
    print("Success!")
}, fail: { () -> () in
    print("Fail!")
})
  • 트레일링 클로저 > 마지막 파라미터의 클로저는 함수 뒤에 붙여 쓸 수 있음
fetchData(success:  { () -> () in
    print("Success!")
}) { () -> () in
    print("Fail!")
}

파라미터가 여러개일 경우 함수 호출 구문 ()을 마음대로 생략해서는 안됨!!!!

🍏 2. 클로저의 경량 문법

  • 문법을 최적화 하여 클로저를 단순하게 쓸 수 있게 하는 것
func doSomething(closure: (Int, Int, Int) -> Int) {
    closure(1, 2, 3)
}
  • 함수 호출
dosomdthing(closure: { (a: Int, b: Int, c: Int) -> Int in
	return a + b+ c 
    })

◻️ 2-1. Parameter Type과 Return Type을 생략

dosomething(closure: { (a, b, c) in
	return a + b + c 
    })

◻️ 2-2. Parameter Name은 $으로 대체하고, 이 경우 Parameter Name과 in 키워드를 삭제한다

dosomething(closure: { (a, b, c) in
	return a + b + c 
    })

a b c 라는 Parameter Name 대신에

a → $0
b → $1
c → $2

$와 index를 이용해 Parameter에 순서대로 접근

doSomething(closure: {  
    return $0 + $1 + $2
})

◻️ 2-3. 단일 리턴문만 남을 경우, return도 생략

  • 단일 리턴문이란?
    • 클로저 내부에 코드가 return 구문 하나만 남은 경우
doSomething(closure: {  
    return $0 + $1 + $2
})
doSomething(closure: {  
     $0 + $1 + $2
})

◻️ 2-4. 클로저 파라미터가 마지막 파라미터면, 트레일링 클로저로 작성

doSomething() {  
     $0 + $1 + $2
}

◻️ 2-5. ()에 값이 아무 것도 없다면 생략

doSomething {  
     $0 + $1 + $2
}

🍏 3. @autoclosure

  • 파라미터로 전달된 일반 구문 & 함수를 클로저로 래핑(Wrapping) 하는 것
  • autoclosure는 파라미터 함수 타입 정의 바로 앞에다가 붙여야 함
    • 실제 클로저를 전달받지 않지만, 클로저처럼 사용이 가능
    • 클로저와 다른 점은 실제 클로저를 전달하는 것이 아니기 때문에 파라미터로 값을 넘기는 것 처럼 ()를 통해 구문을 넘겨줄 수가 있음
func doSomething(closure: @autoclosure () -> ()) {
}
doSomething(closure: 1 > 2)
func doSomething(closure: @autoclosure () -> ()) {
    closure()
}
  • autoclosure를 사용할 경우, 파라미터가 반드시 없어야 함 리턴 타입은 상관 없음!

◻️ 3-1. autoclosure 특징 : 지연된 실행

원래, 일반 구문은 작성되자마자 실행되어야 하는 것이 맞음

근데 autoclosure로 작성하면, 함수 내에서 클로저를 실행할 때까지 구문이 실행되지 않음

왜냐? 함수가 실행될 시점에 구문을 클로저로 만들어주니까

  • autoclosure의 특징은
    원래 바로 실행되어야 하는 구문이 지연되어 실행한다는 특징이 있음

예시!

let closure: () -> String = {
    return "Hello, World!"
}

print(closure()) // "Hello, World!" 출력
func printMessage(_ messageProvider: @autoclosure () -> String) {
    print(messageProvider())
}

printMessage("Hello, World!") // "Hello, World!" 출력

🍏 4. @escaping

  • non-escaping Closure
    • 함수 내부에서 직접 실행하기 위해서만 사용
    • 파라미터로 받은 클로저를 변수나 상수에 대입할 수 없음
    • 중첩 함수에서 클로저를 사용할 경우, 중첩함수를 리턴할 수 없음
    • 함수의 실행 흐름을 탈출하지 않아, 함수가 종료되기 전에 무조건 실행되어야 함
func doSomething(closure: () -> ()) {
}
  • @escaping
    • 함수 실행을 벗어나서 함수가 끝난 후에도 클로저를 실행
    • 중첩함수에서 실행 후 중첩 함수를 리턴
    • 파라미터로 받은 클로저를 변수 상수에 대입하고 싶은 경우 사용
  • 상수에 클로저 대입시 non-escaping Closure 에러가 뜸
func dosomething(closure: () -> ()) {
let f: () -> () = closure
}						// Error "non-escaping Closure"
func doSomething(closure: @escaping () -> ()) {
let f: () -> () = closure
}
  • 중첩함수 내부에서 매개변수로 받은 클로저를 사용할 경우 중첩함수를 리턴할 수 없다는 에러가 뜸
func outer(closure: () -> ()) -> () -> () {
	func inner() {
    	closure()
    }
    return inner
 }				//중첩함수를 리턴할 수 없다는 에러가 뜸
func outer(closure: @escaping () -> ()) -> () -> () {
	func inner() {
    	closure()
    }
    return inner
 }

💡주의 : escaping 클로저 _ 메모리 관리💡

만약 함수가 종료된 후 클로저를 실행하는데, 이때 클로저가 함수 내부 값을 사용함

그럼 이때 함수는 이미 종료 되었는데, 클로저는 함수 내부 값을 어떻게 사용할까?

profile
개발이 어려운 나를 위한... 개발노트

2개의 댓글

comment-user-thumbnail
2024년 6월 19일

특강 정리하신건가요?! 정리가 너무 깔끔해서 종종 참고하러 오겠습니당

1개의 답글