접근 제한자 , mutating , 클로저 , 고차함수

접근제한자

클래스, 구조체, 열거형, 포로토콜 등 접근을 제어하는데 사용된다.

Open: 가장 넓은 접근 수준을 가지며, 해당 멤버가 선언된 모듈 외부에서도 상속하고 재정의할 수 있다.

Public: 해당 멤버가 선언된 모듈은 외부에서도 접근할 수 있지만, 재정의는 할 수 없다. 일반적으로 프레임워크의 API 로 공개될 때 사용한다.

Internal : 기본 접근 수준이며 동일한 모듈 내에서만 접근이 가능하다.

File-private: 같은 소스 파일 내에서만 접근할 수 있습니다. 다른 소스 파일에서는 접근할 수 없습니다.

Private: 가장 제한적인 접근 수준을 가지며, 같은 선언 블록내에서만 접근할 수 있다.

이러한 접근 제한자는 코드의 가시성과 모듈간의 의존성을 관리하고 코드의 안전성과 유지 보수성을 높이는데 도움이 된다. 올바른 접근 제한자를 선택하면 다른 개발자들이 해당 코드를 사용할 때 예상치 못한 문제를 피할 수 있으며, 코드를 더욱 안전하고 읽기 쉽게 만들 수 있다.

mutating

mutating은 구조체나 열거형 내부에서 해당 인스턴스를 변경할 수 있도록 허용하는 키워드이다.

일반적으로 구조체나 열거형은 값 타입으로 메서드 내 인스턴스 속성을 변경할 수 없다. 그러나 특정 경우에는 인스턴스의 속성을 변경해야하는 경우가 있는데 이럴때 mutating 을 사용하여 해당 인스턴스의 속성을 변경할 수 있다.

struct Point {
    var x = 0.0
    var y = 0.0
    
    mutating func moveBy(x deltaX: Double, y deltaY: Double) {
        x += deltaX
        y += deltaY
    }
    
}

//출력값
var point = Point(x: 1.0, y: 2.0)
point.moveBy(x: 2.0, y: 3.0)
print(point)  // 출력 (x: 3.0, y: 5.0)

클로저 (Closure)

클로저는 코드 블록이고 이 코드 블록은 다른 곳에서 사용할 수 있는 독립적인 기능을 담고 있다.

우리는 함수를 변수에 할당하거나 함수의 인자로 전달할 수 있는데, 클로저 역시 이와 같은 방식으로 사용할 수 있다.

간단히 말해 클로저는 코드 조각이며, 이 코드 조각을 특정 상황에서 실행할 수 있습니다.

예를 들어 정렬 기능을 수행하는 함수를 작성할 때 우리는 비교하는 방법을 지정해야합니다. 클로저는 이러한 비교 기능을 간단하게 작성 할 수 있도록 도와줍니다.

클로저는 중괄호로 둘러싸여 있고, 중괄호 안에 매개변수 목록과 실행 코드가 포함됩니다.

{ (parameters) -> ReturnType in
    statements
}

여기서 parameters 는 클로저가 사용할 매개변수를 나타내며, ReturnType 은 클로저가 반환할 값의 타입을 나타낸다. in 키워드는 매개변수와 반환 타입을 클로저의 본문으로부터 분리한다.

예를 들어 숫자를 두개 받아서 더하는 클로저는 아래와 같이 표현할 수 있다.

let addClosure = { (a: Int, b: Int) -> Int in
    return a + b
}

이제 이 클로저를 사용하여 두 숫자를 더할 수 있다.

let result = addClosure(10, 20) // result 는 30

클로저는 이와 같이 코드를 간결하게 만들고 함수형 프로그래밍 패러다임을 지원하는 강력한 도구

주로 함수의 매개변수로 전달되거나, 함수의 반환 값으로 사용되며, 주변 환경을 캡처하여 실행될 수 있다.

Closure 캡처

값 캡처는 주변 값을 복사하여 사용하고, 참조 캡처는 주변의 객체에 대한 참조를 유지하며 사용합니다. 값 캡처는 값을 복사하기 때문에 클로저가 만들어진 시점의 값을 유지하고, 참조 캡처는 주변의 객체에 대한 참조를 유지하기 때문에 해당 객체의 변경사항이 반영됩니다.

값 캡쳐(Value Capture)

  • 값 캡처는 클로저가 주변에 있는 값을 캡처할때 해당 값의 복사본을 저장합니다.
  • 클로저가 생성될 때 주변의 값은 복사되어 클로저 내부에서 사용됩니다.
  • 이후에 값이 변경되더라도 클로저는 복사된 값을 그대로 유지합니다.
  • 주로 값형식(구초제, 열거형)의 인스턴스를 캡처할 때 발생합니다.
  • 값 캡처는 주변의 값을 캡처한 시점의 값으로 사용합니다.
func makeIncrementer(startingValue: Int) -> () -> Int {
    var value = startingValue // 외부에서 전달된 값 캡처 (값만 가져온다 )
    
    let incrementer: () -> Int = {
        value += 1  // 클로저가 캡처한 값 사용
        return value
    }
    
    return incrementer
    
}

참조 캡처(Reference Capture)

  • 참조 캡처는 클로저가 주변에 있는 참조를 캡처합니다.
  • 캡처된 참조는 주변의 객체를 가리키며, 해당 객체에 대한 변경사항은 클로저에서도 반영된다.
  • 주로 참조형식(클래스, 인스턴스 등)의 인스턴스를 캡처할 때 발생한다.
  • 참조 캡처는 주변의 값을 ‘실제 참조하는 객체’로 사용한다.
class Person {
    var name: String
    
    init(name: String) {
        self.name = name
    }
}

var person = Person(name: "Alice")

let printName: () -> Void = {
    print(person.name) //클로저가 캡처한 참조 사용
}

person.name = "Bob"
printName() // "Bob"

위 예시에서 printName 클로저는 person 객체의 name 속성을 캡처합니다. 이후 person 객체의 name 이 변경되어도 클로저는 캡처한 참조를 유지하며 해당 객체의 변경사항이 반영된다.

Closure 매개변수 축약 사용

정렬

let numbers = [10, 5, 8, 2, 7]

//오름차순 정렬
let ascending = numbers.sorted { $0 < $1 }

//내림차순 정렬
let descending = numbers.sorted { $0 > $1 }

필터링

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

//짝수만 필터링
let evens = numbers.filter { $0 % 2 == 0 }

//홀수만 필터링
let odds = numbers.filter { $0 % 2 != 0 }

맵핑

let numbers = [1, 2, 3, 4, 5]

// 각 요소에 2를 곱한 결과 배열 생성
let doubled = numbers.map { $0 * 2 }

// 각 요소에 1을 더한 결과 배열 생성
let plus = numbers.map { $0 + 1 }

고차함수

map 함수

map 함수는 배열 또는 컬렉션의 각 요소에 대해 동일한 작업을 수행하고 그 결과를 새로운 배열로 반환하는 고차함수.

각 요소에 함수를 적용하여 변환된 값으로 이루어진 새로운 배열을 생성한다.

let numbers = [1, 2, 3, 4, 5]
let squaredNumbers = numbers.map { $0 * $0 }
print(squaredNumbers) // 출력 : [1, 4 ,9, 16, 25]

예제는 map 함수를 사용하여 numbers 배열의 각 요소를 제곱, 새로운 배열인 squaredNumbers 를 생성했습니다. 클로저 { $0 * $0 } 은 각 요소를 제곱하는 작업

map 함수는 배열 뿐 만 아니라 다양한 컬렉션 타입에서도 사용 가능하며, 각 요소에 대해 동일한 변환 작업을 수행하는데 유용하다.

filter 함수

filter 함수는 배열이나 컬렉션에서 주어진 조건을 만족하는 요소들만 걸러내어 새로운 배열을 반환하는 고차함수이다. 주어진 조건에 따라 요소를 걸러내는데 사용

let numbers = [1, 2, 3, 4, 5]
let evenNumbers = numbers.filter { $0 % 2 == 0 }
print(evenNumbers) // [2 , 4]

예제는 filter 함수를 사용하여 numbers 배열에서 짝수만을 걸러내어 새로운 배열을 생성했습니다. 클로저 { $0 % 2 == 0 } 는 각 요소가 짝수인지 여부를 판별하는 조건을 나타낸다.

filter 함수를 사용하면 특정 조건에 맞는 요소들을 간편하게 걸러낼 수 있으며, 그 결과로 조건을 만족하는 요소로 이루어진 새로운 배열을 얻을 수 있다.

reduce 함수

reduce 함수는 컬렉션(배열 , 딕셔너리 등) 의 모든 요소를 하나의 값으로 결함하는 고차 함수이다. 조어진 초기값과 클로저를 사용하여 컬렉션의 요소를 순회하면서 값을 누적시키고, 최종적으로 하나의 값으로 반환한다.

let numbers = [1, 2, 3, 4, 5]
let sum = numbers.reduce(0) { $0 + $1 }
print(sum) // 15

let numbers2 = [1, 2, 3, 4, 5, 6, 7]
let sum2 = numbers2.reduce(0, +)
print(sum2)  // 28

예제와 같이 reduce 함수의 경우, 결과적으로 모든 요소를 더한 총합이 출력된다.