새소식

iOS/Swift

[Swift] 클로저

  • -

 클로저(closure)


- C언어와 Objective-C의 block과 lamda와 유사


- 일정 기능을 하는 코드를 하나의 블록으로 모아놓은 것을 말함

 

- 변수나 상수가 선언된 위치에서 참조를 획득하고 저장할 수 있음

 

- iOS에서 클릭 이벤트를 실행할 때 사용

 

- 클로저의 형태

  1. 이름이 있으면서 어떤 값도 획득하지 않는 전역함수의 형태
  2. 이름이 있으면서 다른 함수 내부의 값을 획득할 수 있는 중첩된 함수의 형태
  3. 이름이 없고 주변 문맥에 따라 값을 획득할 수 있는 축약 문법으로 작성된 형태

 

클로저 표현 방법


- 클로저의 위치에 따라 기본 클로저와 후행 클로저로 구분

 

- 각 표현내에서 가독성을 해치지 않는 선에서 표현을 생략하거나 축약 가능

 

- 기본 클로저의 표현 방법

 

{ ( 매개변수들 ) -> 반환 타입 in
     실행 코드
}

 

- 함수와 마찬가지로 입출력 매개변수를 사용 가능

 

- 매개변수의 이름을 지정한다면 가변 매개변수도 사용 가능

 

- 매개변수의 기본값은 사용 불가

 

- 기본 클로저 사용

func printMyName(name1: String, name2: String)-> Void{
	print("name1:\(name1), name2:\(name2)")
}

printMyName(name1: "홍길동", name2: "변사또")
// name1: 홍길동, name2: 변사또

var myClosure: (String, String) -> Void = {
	(name1: String, name2: String) -> Void in
	print("name1:\(name1), name2:\(name2)")
}

myClosure("홍길동", "변사또")
// name1: 홍길동, name2: 변사또

 

 

 

후행 클로저(Tralling Closure)


- 클로저가 조금 길어지거나 가독성이 조금 떨어질 때 사용

 

- Xcode에서 자동완성 기능을 사용하면 자동으로 후행 클로저를 유도

 

- 맨 마지막 전달인자로 전달되는 클로저에만 해당됨

→ 전달인자로 클로저 여러 개를 전달할 때는 맨 마지막 클로저만 후행 클로저로 사용가능

 

- 매개변수에 클로저가 여러 개 있는 경우 → 다중 후행 클로저 문법 사용 가능

 

- 후행 클로저 사용

func calc(a: Int, b: Int, method: (Int, Int) -> Int) -> Int{
	return method(a, b)
}

var result = calc(a: 10, b: 20, method: {
	(a: Int, b: Int) -> Int in
    return a + b // 30
  
})

result = calc(a: 10, b: 20){
	(a: Int, b: Int) -> Int in
	return a + b // 30
}

 

 

 

클로저 표현 간소화


- 문맥을 이용한 타입 유추 : 매개변수의 타입이 생략됨

let reversed: [String] = names.sorted{(first, second) in
	return first > second
}

 

- 단축 인자 이름 : 첫 번째 전달인자부터 $0, $1, $2, $3, ··· 순서로 $와 숫자의 조합으로 표현

let reversed: [String] = names.sorted{
	return $0 > $1
}

→ 매개변수 및 반환 타입과 실행 코드를 구분하기 위해 사용했던 키워드 in 사용할 필요 없음

 

- 암시적 반환 표현 : return 키워드 생략 가능

let reversed: [String] = names.sorted{ $0 > $1 }

 

 

- 연산자 함수 : 매개변수의 타입과 반환 타입이 연산자를 구현한 함수의 모양과 동일하다면 연산자만 표기하더라도 알아서 연산하고 변환

let reversed: [String] = names.sorted(by: >)

 

- 값 획득 : 자신이 정의된 위치의 주변 문맥을 통해 상수나 변수를 획득(Capture) 가능함

→ 이것을 통해 주변에 정의한 상수나 변수가 덩 이상 존재하지 않더라도 해당 상수나 변수의 값을 자신 내부에서 참조하거나 수정가능

→ 클로저가 비동기 작업에 많이 사용됨, 비동기 콜백을 작성하는 경우에 현재 상태를 미리 획득해두지 않으면
실제로 클로저의 기능을 실행하는 순간에는 주변의 상수나 변수가 이미 메모리에 존재하지 않는 경우가 발생

 

-  중첩 함수도 하나의 클로저 형태 → 자신을 포함하는 함수의 지역변수나 지역상수를 획득할 수 있음

 

 

 

클로저는 참조 타입


- 함수나 클로저를 상수나 변수에 할당할 때마다 사실은 상수나 변수에 함수나 클로저의 참조를 설정하는 것

 

 

탈출 클로저(Escaping Closure)


- 함수의 전달인자로 전달한 클로저가 함수 종료 후에 호출 될 때 클로저가 함수를 탈출(Escape)한다고 표현

 

- 클로저를 매개변수로 갖는 함수를 선언할 때 매개변수 이름의 콜론(:) 뒤에 @escapeing 키워드 사용하여

클로저가 탈출을 허용한다고 명시해줄 수 있음

 

- 비동기 작업으로 함수가 종료되고 난 후 호출할 필요가 있는 클로저를 사용해야 할 때 탈출 클로저가 필요함

 

- 정렬할 요소를 비교 연산하기 위해 전달인자로 전달하는 클로저는 비탈출 클로저(Nonescape Closure)

 

- @escaping 키워드를 따로 명시하지 않는다면 매개변수로 사용되는 클로저는 기본으로 비탈출 클로저임

 

- 타입 내부 메서드의 매개변수 클로저에 @escaping 키워드를 사용하여 탈출 클로저임을 명시

 

→ 클로저 내부에서 해당 타입의 프로퍼티나 메서드, 서브스크립트에 접근하려면 self 키워드를 명시적으로 사용

 

- 비탈출 클로저 내부에서 self 키워드는 선택사항

 

- withoutActuallyEscaping(_:do:) : 클로저를 탈출 클로저 인양 사용할 수 있게 돕는 함수

 

- 클래스 인스턴스 메서드에 사용되는 탈출, 비탈출 클로저

typealias VoidVoidClosure = () -> Void

func functionWithNoescapeClosure(losure: VoidVoidClosure){
	closure()
}

func functionWithEscapingClosure(completionHandler: @escaping 
	VoidVoidClosure) -> VoidVoidClosure{
    return completionHandler
}

class SomeClass{
	var x = 10
    
    func runNoescapeClosure(){
	    // 비탈출 클로저에서 self 키워드는 선택사항
    	functionWithNoescapeClosure{ x= 200 }
    }
    
    func runEscapeingClosure() -> VoidVoidClosure {
    	// 탈출 클로저에서는 명시적으로 self를 사용해야함
        return functionWithEscapingClosure { self.x = 100 }
    }
}

let instance: SomeClass = SomeClass()
instance.runNoescapeClosure()
print(instance.x) // 200

let returnedClosure: VoidVoidClosure = instance.runEscapingClosure()
returnedClosure()
print(instance.x) // 100

 


자동 클로저(Auto Closure)


- 함수의 전달인자로 전달하는 표현을 자동으로 변환해주는 클로저

 

- 자동 클로저는 전달인자를 갖지 않음

 

- 클로저가 호출되기 전까지 클로저 내부의 코드가 동작하지 않음 → 연산 지연시킬 수 있음

 

- 연산에 자원을 많이 소모한다거나 부작용이 우려될 때 유용하게 사용 가능 → 코드의 실행을 제어하기 좋음

 

- 클로저를 사용한 연산 지연

var customersInLine: [String] = ["a", "b", "c", "d"]
print(custmersInLine.count) // 4

let customerProvider: () -> String = {
	return customersInLine.removeFirst() // "a"
}

print(customersInLine.count) // 4

print("now serving \(customerProvider()!") // now serving a!
print(customerInLine.count) // 3

 

 

- 클로저가 영영 호출되지 않는다면 내부의 코드도 실행되지 않기 때문에 해당 연산은 실행되지 않음

 

- 같은 조건의 클로저를 함수의 전달인자로 전달하는 예제

var customersInLine: [String] = ["a", "b", "c", "d"]

func serveCustomer(_ customerProvider: () -> String){
	print("now serving \(customerProvider()!")
    // now serving a!
}

serveCustomer({customersInLine.removeFirst()}) // a

 

 

- 코드의 serveCustomer(_:) 함수는 클로저를 매개변수로 전달받고 있음

 

- 자동 클로저는 전달인자를 갖지 않는 반환 타입의 값이 자동 클로저의 매개변수로 전달되면 이를 클로저로 바꿔줄 수 있는 것임

 

- 자동 클로저의 과도한 사용은 코드를 이해하기 어렵게 만듦

 

 

참조 :  Swift 스위프트 프로그래밍 3판 Swift 5, 저자 야곰 (한빛미디어)
인프런 강의 (iOS 최신 앱 개발 강의 - 기초부터 핵심 요소, 간단한 포폴까지 제작)
728x90

'iOS > Swift' 카테고리의 다른 글

[Swift] UnsafePointer, UnsafeMutablePointer  (0) 2023.01.08
[Swift] 소수점 없애기  (0) 2023.01.01
[Swift] 다른 앱 실행시키기  (0) 2022.12.04
[Swift] URLComponent  (0) 2022.11.20
[Swift] URLSession https 인증서 신뢰  (0) 2022.11.20
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.