1. Generic Function
아래와 같은 함수를 보자. 인트 변수를 두개바꿔주는 함수이다.
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
만약 정수형이 아니라 실수형을 바꿔주고 싶다면?
아래처럼 함수바디 구현체는 같은데 함수이름과 파라미터 자료형만 다른걸 세개나 정의해줘야 한다.
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
func swapTwoStrings(_ a: inout String, _ b: inout String) {
let temporaryA = a
a = b
b = temporaryA
}
func swapTwoDoubles(_ a: inout Double, _ b: inout Double) {
let temporaryA = a
a = b
b = temporaryA
}
이는 제네릭으로 해결 가능하다. 자바의 제네릭, c++의 템플릿과 같은것이다.
- 여기서 T는 타입 파라미터 라고 불린다.
- 함수이름 뒤에 <타입파라미터> 를 붙여주어야 한다.
- 타입 파라미터는 T가 아닌 다른 이름을 사용할 수도 있다
- Dictionary<Key, Value> 처럼 여러개의 타입 파라미터를 가질 수 있다.
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let temporaryA = a
a = b
b = temporaryA
}
2. Generic Type
제네릭은 함수뿐만 아니라 타입에도 사용하능하다. 무슨말이냐 하면, struct, class, enumeration에도 사용 가능하다
아래는 Int 스택을 나타내는 코드이다.
struct IntStack {
var items: [Int] = []
mutating func push(_ item: Int) {
items.append(item)
}
mutating func pop() -> Int {
return items.removeLast()
}
}
이는 앞서 본 문제상황처럼 똑같은 문제를 야기한다. double에 필요한 스택을 만든다면 똑같이 중복된 코드를 한번 더 작성해야된다.
이는 제네릭으로 또다시 해결 가능하다
struct Stack<Element> {
var items: [Element] = []
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
}
3. extension과 Genenric
제네릭 타입을 extension할 때는 아래처럼 타입 파라미터를 따로 명시해주지 않아도 사용이 가능하다
extension Stack {
var topItem: Element? {
return items.isEmpty ? nil : items[items.count - 1]
}
}
if let topItem = stackOfStrings.topItem {
print("The top item on the stack is \(topItem).")
}
// Prints "The top item on the stack is tres."
4. Type 제한
아래처럼 타입 파라미터를 특정한 타입형으로 제한할 수 있다.
타입파라미터 옆에 : 세미콜론으로 구분하여 클래스나 프로토콜을 붙이면 된다.
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// function body goes here
}
5. Associated Type과 Generic
프로토콜에서 Associated Type을 이용하면 정해지지 않은 타입에 대해 제한을 걸 수 있다.
아래 예제의 경우 Item이라는게 일단 뭔지는 모르지만, Item이라고 써진 애들은 다같이 같은 타입을 사용해야 한다.
protocol Container {
associatedtype Item
mutating func append(_ item: Item)
var count: Int { get }
subscript(i: Int) -> Item { get }
}
아래는 구현한 예제이다. typealias로 Item이 Int라고 명시적으로 지정해주고 있다.
struct IntStack: Container {
// original IntStack implementation
var items: [Int] = []
mutating func push(_ item: Int) {
items.append(item)
}
mutating func pop() -> Int {
return items.removeLast()
}
// conformance to the Container protocol
typealias Item = Int
mutating func append(_ item: Int) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Int {
return items[i]
}
}
허나 스위프트는 타입추론이 가능하기에, 대충 규격만 잘 맞췄으면 typealias를 생략 가능하다.
이러한 Associated Type에는 Generic도 사용이 가능하다!
struct Stack<Element>: Container {
// original Stack<Element> implementation
var items: [Element] = []
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
// conformance to the Container protocol
mutating func append(_ item: Element) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Element {
return items[i]
}
}
또 associatedtype은 아래처럼 타입제한도 가능하다
protocol Container {
associatedtype Item: Equatable
mutating func append(_ item: Item)
var count: Int { get }
subscript(i: Int) -> Item { get }
}
'iOS > Swift배우기' 카테고리의 다른 글
[Swift] PropertyWrapper 사용법 (0) | 2024.03.22 |
---|---|
[SwiftUI] 자식에게 State 전달하기 (0) | 2024.03.21 |
SwiftUI에서 {} 동작 원리 (0) | 2024.03.20 |
[Swift] inout 파라미터 (0) | 2024.03.20 |
[Swift] Protocol 배우기 (0) | 2024.03.19 |
댓글