210326 Fri

1. 첫 번째 학습 내용: Generics

Generic code를 사용하면 더 유연하고 재사용 가능한 함수와 타입의 코드를 작성할 수 있음

Generic code enables you to write flexible, reusable functions and types that can work with any type, subject to requirements that you define.

Generic Functions

import UIKit

func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
    let temporaryA = a
    a = b
    b = temporaryA
}

var someInt = 3
var anotherInt = 107
print("스왑 전: someInt의 값은 \(someInt)이고, anotherInt의 값은 \(anotherInt)입니다.")
// 스왑 전: someInt의 값은 3이고, anotherInt의 값은 107입니다.
swap(&someInt, &anotherInt)
print("스왑 후: someInt의 값은 \(someInt)이고, anotherInt의 값은 \(anotherInt)입니다.")
// 스왑 후: someInt의 값은 107이고, anotherInt의 값은 3입니다.

var someString = "좋아합니다"
var anotherString = "귤을"
print("스왑 전: \(someString) \(anotherString)")
// 스왑 전: 좋아합니다 귤을
swap(&someString, &anotherString)
print("스왑 후: \(someString) \(anotherString)")
// 스왑 후: 귤을 좋아합니다

위의 예시에서 제네릭을 쓰지 않았다면? Int가 들어오면 스왑해주는 함수 따로, String이 들어오면 스왑해주는 함수 따로 매번 만들어줘야 해서 번거로움 😢

Type Parameters (타입 매개변수)

제네릭 함수는 실제 타입 이름(Int, String 등)을 써주는 대신 Placeholder(위 함수에서는 T)를 사용함. Placeholder는 타입의 종류를 알려주진 않지만 말 그대로 어떤 타입이라는 것은 알려줌. 즉, 매개변수로 Placeholder 타입이 T인 두 매개변수가 있으므로, 두 매개변수는 같은 타입이라는 정도는 알 수 있음. T의 실제 타입은 함수가 호출되는 그 순간 결정됨.

Generic Types

pushing → 새로운 값이 들어간다

popping → collection의 끝 값 (스택 구조에서 맨 위에 쌓여있는 값)이 제거된다




import UIKit

struct Stack<Element> {
    var items = [Element]()
    mutating func push(_ item: Element) {
        items.append(item)
    }
    mutating func pop() -> Element {
        return items.removeLast()
    }
}

var stackOfStrings = Stack<String>()
stackOfStrings.push("uno")
stackOfStrings.push("dos")
stackOfStrings.push("tres")
stackOfStrings.push("cuatro")
print(stackOfStrings) //Stack<String>(items: ["uno", "dos", "tres", "cuatro"])

let fromTheTop = stackOfStrings.pop()
print(stackOfStrings) // Stack<String>(items: ["uno", "dos", "tres"])

스택 맨 위의 값이 pop한 후 없어진 걸 알 수 있음.

타입 제한 (Type Constraints)

func substractTwoValue<T: BinaryInteger>(_ a: T, _ b: T) -> T {
    return a - b
}

substractTwoValue(10, 1) // 9

뺄셈을 하려면 뺄셈 연산자를 사용할 수 있는 타입이어야 연산이 가능함.

즉, T가 실제로 받아들일 수 있는 타입은 뺄셈 연산자를 사용할 수 있는 타입이어야 함.

타입 매개변수인 T의 타입을 BinaryInteger 프로토콜을 준수하는 타입으로 한정해두니 뺄셈 연산이 가능하게 됨.

이처럼 타입 제약은 함수 내부에서 실행해야 할 연산에 따라 적절한 타입을 전달받을 수 있도록 제약을 둘 수 있음.

2. 두 번째 학습 내용: Subscripts

  • 클래스, 구조체, 열거형에는 컬렉션, 리스트 시퀀스 등 타입의 요소에 접근하는 단축 문법인 subscript를 정의할 수 있음.
  • 서브스크립트는 별도의 Setter 또는 Getter 등의 메서드를 구현하지 않아도 인덱스를 통해 값을 설정하거나 가져올 수 있음

e.g. someArray라는 Array 인스턴스의 index를 통해 해당 인덱스의 값에 접근하고 싶다면 someArray[index]라고 표현하며, someDictionary라는 Dictionary의 key를 통해 해당 키의 값을 가져오고 싶다면 someDictionary[key]라고 표현하는 것이 바로 subscripts.

struct TimesTable {
    let multiplier: Int
    subscript(index: Int) -> Int {
        return multiplier * index
    }
}
let threeTimesTable = TimesTable(multiplier: 3)
print("six times three is \(threeTimesTable[6])") //18

자료 출처

좋은 웹페이지 즐겨찾기