-
Swift4: Protocol Oriented Programming - 4.제네릭POP 2021. 2. 15. 23:36
4장 제네릭
제네릭은 중복을 피하고, 유연하고 재사용 가능한 코드를 작성할 수 있게 해준다. 스위프트의 Array나 Set 같은 경우 모두 제너릭 구조체로 구현되어 있다.
enum Optional<T> { case None case Some(T) }
제네릭의 대표적 예시는 옵셔널이다. 옵셔널에 nil을 대입하면 None 값을 갖고, 어떠한 값을 대입하면 T 타입의 Some 값을 갖게 된다.
제네릭 함수
제네릭 함수를 이용하여 타입 마다 같은 기능을 하는 함수를 계속해서 만들 필요 없이, 중복을 최소화하여 구현할 수 있다.
func swapGenerics<T>(a: inout T, b: inout T) { let tmp = a a = b b = tmp }
위에서
T
는 플레이스홀더로 스위프트에게 타입을 런타임 단계에서 정의할 것이라고 알려준다.func genericEqual<T>(a: T, b: T) -> Bool{ return a == b }
다음 함수는 컴파일되는 시점에 인자의 타입을 모르기 때문에 동등 연산자를 사용할 수 있는지를 알지 못하여 에러가 발생한다. 이럴 경우 타입 제약을 통해 해결할 수 있다.
제네릭 타입 제약
타입 제약을 통해 부모 클래스나 프로토콜에 정의된 메소드나 프로퍼티를 사용할 수 있다.
func testGenericComparable<T: Comparable>(a: T, b: T) -> Bool { return a == b }
제네릭 타입
어떠한 타입과도 동작이 가능한 클래스나 구조체, 열거형. 인스턴스를 생성할 때 타입을 명시하며, 해당 인스턴스 동안에는 타입 변경이 불가하다.
struct List<T> { var items = [T]() mutating func add(item: T) { items.append(item) } }
제네릭 타입 내에서 제네릭 메소드를 정의할 때에는 함수 이름 뒤에 와 같은 플레이스홀더 선언을 하지 않아도 되며, 타입 선언에서 정의한 플레이스홀더와 동일한 홀더를 사용하면 된다.
연관 타입
프로토콜 내에서 타입 대신에 사용될 수 있는 플레이스홀더명. 제네릭과 비슷하게 사용되지만 프로토콜을 위한 연관 타입은
associatedtype
키워드를 사용한다.protocol MyAssociatedProtocol { associatedtype E var items: [E] {get set} mutating func add(item: E) } struct MyIntType: MyAssociatedProtocol { var items: [Int] = [] mutating func add(item: Int) { items.append(item) } }
연관 타입을 가지는 프로토콜을 채택하는 제네릭 타입을 만들 수도 있다.
struct MyGenericType<T>: MyAssociatedProtocol { var items: [T] = [] mutating func add(item: T) { items.append(item) } }
제네릭 서브스크립트
타입에서 정의한 제네릭 타입을 서브스크립트 내에서 사용할 수 있고, Swift4부터 서브스크립트 정의에 제네릭 타입을 정의할 수 있다.
extension List { subscript(index: Int) -> T? { return getItemAtIndex(index: index) } subscript<E: Sequence>(indices: E) -> [T] where E.Iterator.Element == Int { var result = [T]() for index in indices { result.append(items[index]) } return result } }
커스텀 타입에 Copy-on-write 구현하기 🌟
COW의 자세한 내용은
https://welly-log.tistory.com/8
스위프트의 모든 자료구조 (Array, Dictionaty, Set)에는 COW 기능이 구현되어 있다. 그러나 커스텀 값 타입은 이런 기능을 자동으로 갖지는 못한다.
isKnownUniquelyReferenced()
- 참조 타입의 인스턴스에 참조가 오직 하나일 때는 true, 하나 이상일 때 false 반환사용하려는 타입을 값 타입으로 정의하고, 내부 프로퍼티를 참조 타입으로 가지고 있는 형태로 COW 구현이 가능하다. 프로퍼티의 변경이 있는 작업을 할 때 위의
isKnownUniquelyReferenced()
함수를 이용하여 체크해 준 뒤, 참조가 하나 이상일 경우 복사본을 해당 프로퍼티에 할당한다.struct Queue { private var internalQueue = BackendQueue<Int>() public mutating func addItem(item: Int) { checkUniquelyReferencedInternerQueue() internalQueue.addItem(item: item) } public mutating func getItem() -> Int? { checkUniquelyReferencedInternerQueue() return internalQueue.getItem() } public func count() -> Int { return internalQueue.count() } mutating private func checkUniquelyReferencedInternerQueue() { if !isKnownUniquelyReferenced(&internalQueue) { internalQueue = internalQueue.copy() print("Making a copy of internalQueue") } else { print("Not making a copy of internalQueue") } } mutating public func uniquelyReferenced() -> Bool { return isKnownUniquelyReferenced(&internalQueue) } public func printItems() { internalQueue.printItems() } }
자료 구조가 수 많은 아이템을 갖게 될 때, COW 기능을 포함한 자료 구조를 사용하자 !
코드
참고 문헌
https://book.naver.com/bookdb/book_detail.nhn?bid=14107671
'POP' 카테고리의 다른 글
Swift4: Protocol Oriented Programming - 6.프로토콜지향 프로그래밍 (0) 2021.02.17 Swift4: Protocol Oriented Programming - 5.객체지향 프로그래밍 (0) 2021.02.16 Swift4: Protocol Oriented Programming - 3.확장 (0) 2021.02.15 Swift4: Protocol Oriented Programming - 2.타입 선택 (0) 2021.02.14 Swift4: Protocol Oriented Programming - 1.프로토콜 시작 (0) 2021.02.12