728x90
Generic이란
특정 타입에 의존하지 않고, 다양한 타입에서 동작할 수 있도록 구현하는 기능을 말한다.
이를 이용하면 코드의 재사용성과 타입 안정성을 높일 수 있다.
Swift에서는 이를 함수, 메서드, 클래스, 구조체, 열거형 등 다양한 곳에서 사용이 가능하다.
쉽게 말해서 여러 타입의 값이 들어왔을 때 이를 규정하지 않고 다양하게 받아 처리하게 해준다는 뜻이다.
- 제너릭 함수
- inout 매개변수
- 튜플을 이용한 값 교환
- 제너릭 메서드
- 구조체에서의 제너릭 메서드
- 제너릭 클래스
- 제너릭을 이용한 클래스 설계
- 제너릭 열거형
- 제너릭을 활용한 Result 타입
- 제너릭 구조체
- 여러 개의 타입을 받는 구조체
- 동일한 타입을 제한하는 Comparable 사용
- 제너릭과 프로토콜
- associatedtype을 활용한 프로토콜 정의
- 프로토콜과 제너릭
- DictionaryWrapper를 통한 프로토콜과 제너릭 활용
- 제너릭 서브스크립트
- subscript를 이용한 값 접근
- 제너릭 확장
- 기존 구조체 확장에서 제너릭 활용
1. 제너릭 함수 처리
// T : 타입 파라미터이며, 함수가 호출될 때 전달된 타입으로 대체
func swapValues<T>(_ a: inout T, _ b: inout T) {
let temp = a
a = b
b = temp
}
var x = 10
var y = 20
// &x, &y : inout 매개변수로 값을 전달할 때, 해당 변수의 메모리 주소를 직접 참조하도록 표시
swapValues(&x, &y)
print(x, y) // 20, 10
var str1 = "Hello"
var str2 = "World"
swapValues(&str1, &str2)
print(str1, str2) // "World", "Hello"
inout(&) 매개변수는 기본적으로 let이 아닌 var 로 전달해야 한다.
또한 함수 내에서 복사되지 않고 직접 수정되므로, 함수가 종료될 때까지 원본 변수에 접근할 수 없다.
func testInout(_ a: inout Int, _ b: inout Int) {
a = b // 가능
// b = a // 같은 변수를 중복해서 사용 할 수 없으므로 오류가 남.
}
만약 & 쓰기 싫은데 -.-? 할 수 도 있는데, 튜플 방식을 사용해도 된다고 한다.
근데.. 그냥 & 쓰는게 낫지 않을까?🤔
// & 쓰기 싫어요.
func swapValues<T>(_ a: T, _ b: T) -> (T, T) {
return (b, a)
}
var x = 10
var y = 20
(x, y) = swapValues(x, y)
print(x, y) // 20, 10
2. 제너릭 메서드
struct Stack<Element> {
private var items: [Element] = []
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element? {
return items.popLast()
}
}
var intStack = Stack<Int>()
intStack.push(1)
intStack.push(2) // [1, 2] 가 들어있다.
print(intStack.pop() ?? "pop할게 없어!") // 1이 pop 된다 -> print에는 2
print(intStack.pop() ?? "pop할게 없어!") // 2가 pop 된다 -> "pop할게 없어!"
Element는 Stack이 사용할 데이터 타입을 의미하며, 실행 시점에 결정된다.
push()와 pop()은 Element 타입을 사용하여 다양한 타입의 데이터를 다룰 수 있다.
3. 제너릭 클래스
// 다양한 타입을 처리할 수 있는 클래스
class Box<T> {
var value: T
init(_ value: T) {
self.value = value
}
func getValue() -> T {
return value
}
}
let intBox = Box(1234)
print(intBox.getValue()) // 1234
let strBox = Box("Hello, World")
print(strBox.getValue()) // "Hello, World"
<T> : 타입 파라미터
value: T : T 타입의 프로퍼티를 가짐
getValue() : T 타입의 값을 반환함
4. 제너릭 열거형
// 제너릭 열거형
enum Result<T> {
case success(T)
case failure(String)
}
let successResult: Result<Int> = .success(100)
let failureResult: Result<String> = .failure("Error Occurred")
switch successResult {
case .success(let value):
print("Success with value \(value)")
case .failure(let message):
print("Failure: \(message)")
}
<T> : 성공했을 때 T 타입의 데이터를 담음
.success(T), .failure(String) : 서로 다른 타입을 받아서 처리
5. 제너릭 구조체
struct Pair<T, U> {
let first: T
let second: U
}
let pair = Pair(first: "Apple", second: 3500)
print(pair.first) // "Apple"
print(pair.second) // 3500
<T, U> : 두 개의 타입을 다룰 수 있다고 정의해 둔 것이다.
만약, 두 개의 값은 같게 받고 싶다면?
func findLargest<T: Comparable>(_ a: T, _ b: T) -> T {
return a > b ? a : b
}
print(findLargest(10, 20)) // 20
print(findLargest("apple", "banana")) // "banana"
<T: Comparable> : T가 Comparable 프로토콜을 준수해야 함을 의미
a > b 비교가 가능해야 하므로 제한을 걸어줌
6. 제너릭과 프로토콜
protocol Container {
associatedtype Item
mutating func append(_ item: Item)
func count() -> Int
}
struct IntContainer: Container {
var items: [Int] = []
mutating func append(_ item: Int) {
items.append(item)
}
func count() -> Int {
return items.count
}
}
var container = IntContainer()
container.append(5)
print(container.count()) // 1
associatedtype Item : 프로토콜 내부에서 사용할 타입을 정의함
IntContainer는 Int 타입을 가지도록 구현한다
7. 프로토콜과 제너릭
struct DictionaryWrapper<Key: Hashable, Value> {
private var dict: [Key: Value] = [:]
subscript(key: Key) -> Value? {
get { return dict[key] }
set { dict[key] = newValue }
}
}
var myDict = DictionaryWrapper<String, Int>()
myDict["age"] = 25
print(myDict["age"]!) // 25
associatedtype Item 을 사용하여 프로토콜 내부에서 사용할 타입을 정의
IntContainer 는 Int타입을 가지도록 구현한다는 의미
8.제너릭 서브스크립트
struct DictionaryWrapper<Key: Hashable, Value> {
private var dict: [Key: Value] = [:]
subscript(key: Key) -> Value? {
get { return dict[key] }
set { dict[key] = newValue }
}
}
var myDict = DictionaryWrapper<String, Int>()
myDict["age"] = 25
print(myDict["age"]!) // 25
Key: Hashable 을 사용하여 키가 Hashable을 준수하도록 제한
subscript(key: Key) -> Value? 를 사용하여 키를 이용해 값에 접근함.
9. 제너릭 확장
extension Stack {
var topItem: Element? {
return items.last
}
}
var stack = Stack<Int>()
stack.push(10)
stack.push(20)
print(stack.topItem) // 20
Stack 구조체에 topItem 프로퍼티를 추가하여 마지막 요소를 반환하는 기능
-> Element 타입을 그대로 사용하여 유연성을 유지함.
associatedtype은 프로토콜에서 사용할 타입을 추후에 지정하도록 하는 기능.
제너릭(Generic)과 비슷한 개념이지만, 프로토콜에서 특정 타입을 미리 정하지 않고, 이를 채택하는 타입이 정의하도록 함
-> 더 유연한 설계가 가능
subscript는 배열(Array)처럼 인덱스를 사용하여 값에 접근할 수 있도록 해주는 기능.
컬렉션, 리스트, 사전(Dictionary) 같은 타입에서 값을 인덱스 또는 키를 통해 읽고 쓰는 방법을 제공.
// associatedtype 선언 예제
protocol Container {
associatedtype Item // 연관 타입 선언
var items: [Item] { get set }
mutating func append(_ item: Item)
func getItem(at index: Int) -> Item
}
// subscript 기본 예제
subscript(index: Int) -> 타입 {
get {
// 반환할 값 정의
}
set(newValue) {
// 새 값 저장하는 코드
}
}
728x90
'iOS > Swift' 카테고리의 다른 글
Swift 함수와 Clousure 의 예제코드 (0) | 2025.03.26 |
---|---|
Swift 에서의 프로퍼티(Property) (0) | 2025.03.21 |
SwiftUI 로 고차함수 예제코드 (0) | 2025.03.19 |
Swift 에서의 초기화와 초기화 해제 (init, deinit) 그리고 self (0) | 2025.03.19 |
Swift 에서의 일급 함수 (First-Class Function) (0) | 2025.03.18 |