반응형

참고 사이트: iOS 프로그래밍을 위한 스위프트 기초(edwith)
참고 문서: The Swift Language Guide (한국어)
실행 환경: Xcode 12 실행 → Create a new Xcode project → macOS → Command Line Tool 선택 → Product Name, Organization Identifier 입력 후 프로젝트 생성

 

목차

  1. 스위프트(Swift) 5 배워보기 - 1. 다양한 타입
  2. 스위프트(Swift) 5 배워보기 - 2. 함수, 제어문, 옵셔널
  3. 스위프트(Swift) 5 배워보기 - 3. 클래스, 구조체, 열거형
  4. 스위프트(Swift) 5 배워보기 - 4. 클로저
  5. 스위프트(Swift) 5 배워보기 - 5. 프로퍼티
  6. 스위프트(Swift) 5 배워보기 - 6. 상속, 인스턴스 생성/소멸
  7. (계속)

 


클로저

  • 클로저(Closure)는 코드 블록이다.
  • 일급 객체로 전달인자, 변수, 상수 등에 값 저장 및 전달이 가능하다.
  • 어떤 상수나 변수의 참조를 캡처해 저장할 수 있다. -> 값 캡처(Capturing Values)
  • 주로 함수의 전달 인자로 많이 사용된다.
  • 함수는 이름이 있는 클로저이다.

 

클로저 형태

  • 클로저는 다음 세 가지 형태 중 하나를 갖는다.

1. 전역 함수(global functions): 이름이 있고 어떤 값도 캡처하지 않는 클로저
2. 중첩 함수(nested function): 이름이 있고 관련한 함수로부터 값을 캡처할 수 있는 클로저
3. 클로저 표현: 경량화된 문법으로 쓰이고 관련된 문맥(context)으로부터 값을 캡처할 수 있는 이름이 없는 클로저

 

클로저 문법

{ (인자 값 목록) -> 반환타입 in
	실행코드
}

// 함수와 클로저의 차이
// - 함수를 사용할 경우
func sumFunction(a: Int, b: Int) -> Int {
	return a + b
}

var sumResult: Int = sumFunction(a: 1, b: 2)
print(sumResult) // 3


// - 클로저를 사용할 경우
var sum: (Int, Int) -> Int = { (a: Int, b: Int) in
	return a + b
}

sumResult = sum(1, 2)
print(sumResult) // 3

 

클로저 표현(Expressions)

  • 클로저는 다양한 형태로 표현될 수 있다.
  • 아래 예제는 일반적인 함수로 sorted(by:) 라이브러리를 사용하는 경우이다.
// 예를 들어, Swift의 표준 라이브러리에는 sorted(by:) 라는
// 알려진 타입의 배열 값을 정렬하는 메소드를 제공한다.
// 이때 by에 어떤 방법으로 정렬을 수행할지를 기술한 클로저를 넣으면,
// 그 방법대로 정렬된 배열을 얻을 수 있다.

// String 타입의 names 배열을 생성
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]

// names의 아이템은 String 타입이므로
// String 인자와 Bool 반환 타입을 갖는 클로저를 사용해야 한다.
// (String, String) -> Bool

// 이름을 역순으로 정렬하고 싶을 경우
// 인자로 전달받은 첫 이름이 다음 이름보다 크면 True, 아니면 False를 반환하는
// 함수를 하나 만든다. <-- 클로저를 제공하는 일반적인 방법이 함수이다.
func backward(_ s1: String, _ s2: String) -> Bool {
	return s1 > s2
}

// 이제 생성한 backward 함수(클로저)를 by에 넣는다.
var reversedNames = names.sorted(by: backward)

// ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
// 역순으로 정렬된 배열을 얻을 수 있다.

 

  • 위 코드를 클로저를 사용하여 다양하게 표현할 수 있다.
// 1. 인라인 클로저
// 함수로 따로 정의된 형태가 아니라 인자로 바로 들어가 있는 형태
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
  return s1 > s2
})

// 아래처럼 한줄로 적을수도 있다.
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 } )


// 2. 타입추론
// names 배열이 String 배열이기 때문에,
// sorted(by:) 메소드에서 이미 (String, String) -> Bool 타입의
// 인자가 들어와야 하는지 컴파일러가 알고 있다. 따라서 생략가능
reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )


// 3. 단일 표현 클로저에서의 암시적 반환
// 단일 표현 클로저에서는 return 을 생략할 수 있다.
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )


// 4. 인자 이름 축약
// Swift에서는 인라인 클로저에 자동으로 축약 인자 이름(순서에 따라 $0, $1 ... )을 제공한다.
// 3번의 코드를 아래처럼 축약할 수 있다.
reversedNames = names.sorted(by: { $0 > $1 } )


// 5. 연산자 메소드
// Swift에서는 String 타입 연산자에는 String 끼지 비교할 수 있는 비교 연산자(>)를 구현해 두었다.
// 따라서 아래처럼 비교연산자만 적을 수도 있다.
reversedNames = names.sorted(by: >)


// 6. 후위 클로저
// 만약 함수의 마지막 인자로 클로저를 넣는다면 후위 클로저를 사용할 수 있다.
// 함수를 대괄호로 묶어서 그 안에 처리할 내용을 적으면 된다.
reversedNames = names.sorted() { $0 > $1 }

// 여기서 마지막 인자가 클로저고, 후위 클로저를 사용하면 괄호()도 생략할 수 있다.
reversedNames = names.sorted { $0 > $1 }

 

  • 위 예제의 코드들은 모두 결괏값이 동일한 코드이다.
  • 다양한 방식으로 축약하려 클로저를 표현할 수 있지만, 가독성을 고려하여 적당한 선에서 축약하는 것이 좋다.

 

값 캡처(Capturing Values)

  • 클로저는 특정 문맥의 상수나 변수의 값을 캡처할 수 있다.
  • 문자 그대로 값을 캡처하기 때문에, 원본 값이 사라져도 여전히 클로져 내부에서는 그 값을 활용할 수 있다.
  • Swift에서 값을 캡처하는 가장 단순한 형태가 중첩 함수(nested function)이다.
// makeIncrementer 함수 선언
// (forIncrement amount: Int)를 인자로 받고
// () -> Int를 반환한다. <- Int형 클로저 반환
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    
    // makeIncrementer 함수 안에 incrementer 함수가 중첩되어 있다.
    func incrementer() -> Int {
    	// incrementer 함수 안에 선언된 runningTotal, amount가 없지만
        // 클로저 내부의 runningTotal과 인자로 받은 amount가 캡쳐링되었기 때문에 사용가능하다.
        runningTotal += amount
        return runningTotal
    }
    
    return incrementer
}

// 값을 10씩 증가하는 중첩함수를 실행해보면 10을 반환한다.
let incrementByTen = makeIncrementer(forIncrement: 10)

print(incrementByTen()) // 10

// 한 번 더 함수를 실행해보면 20을 반환한다.
print(incrementByTen()) // 20

// 여기서 보면 함수는 개별적으로 실행되었지만,
// runningTotal, amount 변수를 캡쳐링되어 공유하기 때문에
// 결과값이 누적되어 나온다.

 

반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기