안녕하세요.
KataRN입니다.
오늘은 제네릭(Generic)에 대해서 알아보겠습니다.
제네릭(Generic)이란 타입에 의존하지 않는 범용 코드를 작성할 때 사용한다.
Swift 표준 라이브러리의 대다수는 제네릭으로 선언되어 있다고 합니다.(Array, Dictionary도 제네릭타입입니다.)
우선 제네릭이 뭔지에 대해 설명드리기 위한 예제를 만들었습니다.
func showParam(_ a: Int, _ b: Int) {
print(a, b)
}
파라미터가 뭔지 프린트하는 단순한 함수입니다.
보시다싶이 Int인 경우에만 작동합니다.
아래처럼 Double형태인 5.5를 넣었으니 안된다고 하겠죠.
제네릭을 사용하지 않았던 이전의 "나"였으면 아마 함수를 추가했을 것입니다.
func showParam(_ a: Int, _ b: Int) {
print(a, b)
}
func showParam(_ a: Double, _ b: Double) {
print(a, b)
}
이렇게 말이죠.
이것은 오버로드라고 합니다. 오버라이드하고 다릅니다.
오버로드는 같은 함수인데 타입형에 따라 함수가 구분되는 것을 말합니다.
문제는 String으로 넣으면 또 똑같은 일이 일어나고 해결하기 위해서는 String형으로 들어오는 함수를 만들어야겠죠?
그럼 하나의 함수가 9줄 이상이 되겠네요.
제네릭을 알고있는 지금의 저라면 이렇게 함수를 만들겠습니다.
func showParam<T>(_ a: T, _ b: T) {
print(a, b)
}
이렇게 3줄로 끝이납니다...
다시말해 타입에 제한받지 않는 코드를 만들때 사용합니다.
<>꺽쇠를 이용하여 사용하며 일반적으로 Type Parameter를 뜻하는 T를 사용한다고합니다.
(다른것으로 사용해도 되지만 T나 V 같은 단일 문자, 혹은 Upper Camel Case를 사용한다고 합니다.)
제네릭을 이용한 함수는 제네릭함수라고 합니다.
좀더 나아가서 구조체, 클래스, 열거형 타입에도 선언할 수 있고 제네릭 타입이라고 합니다.
그리고 특정 클래스의 하위클래스나 특정 프로토콜을 준수하는 타입만 받을 수 있게 제약을 둘 수 있습니다.
우선 프로토콜 제약을 보겠습니다.
다시 예제를 한번 보시죠.
문제가 없을 줄 알았는데 문제가 생겼습니다.
== 연산자는 a, b 타입이 Equatable란 프로토콜을 준수할때만 사용이 가능하다고합니다.
Equatable 프로토콜을 준수 안하는 경우가 올 수 있으니까 못하게 하는것입니다.
그렇기 때문에 제약을 줘보겠습니다.
이러면 문제가 없어집니다.
너무 신기해...
이번엔 클래스 제약을 보겠습니다.
아래 보시면 클래스들을 정의했고 함수를 정의했습니다.
printName 라는 함수는 Human으로 제약을 추가하였습니다.
class Animal { }
class Human { }
class KataRN: Human { }
func printName<T: Human>(_ a: T) { }
이렇게 하여 함수를 사용해보겠습니다.
animal은 Human이 아니며 상속도 받지 않아서 이처럼 사용이 불가능합니다.
다음은 제네릭 확장에 대해 알아보겠습니다.
제네릭타입인 Array를 확장하고싶다면?
extension Array {
mutating func pop() -> Element {
return self.removeLast()
}
}
구현부에서 타입파라미터가 Element이기 때문에 Element로 사용해야됩니다.
확장에서 제네릭선언이나 다른타입파라미터를 사용하면 아래처럼 안됩니다.
이럴 경우에는 where을 통해 확장 또는 제약을 주면 됩니다.
extension Array where Element: FixedWidthInteger {
mutating func pop() -> Element { return self.removeLast() }
}
이럴 경우에는 Array<Int>에서는 pop()을 사용할 수 있지만 Array<String>는 사용할 수 없습니다.
저는 최근 공모전에 제출한 앱에서 제네릭함수를 사용하지 않고 오버로드만 하였는데
제네릭함수 설정 + 특정타입지정 함수를 오버로드를 하게 되면 어떻게 될까요?
우선순위 : 타입지정된 함수 > 제네릭함수 이기 때문에 타입지정된 함수가 우선실행된다고합니다.
제네릭을 공부하다보니 associatedtype 가 계속 언급됩니다.
- 프로토콜의 일부분으로 타입에 플레이스홀더 이름을 부여합니다. 다시말해 특정 타입을 동적으로 지정해 사용할 수 있습니다.
아래 처럼 지정하면 Item은 어떤 타입도 될 수 있다고합니다.
protocol Container {
associatedtype Item
mutating func append(_ item: Item)
var count: Int { get }
subscript(i: Int) -> Item { get }
}
associatedtype는 좀 헷갈려서 조만간 다시 정리해보도록할게요 ㅠㅠ
오늘도 긴글 읽어주셔서 감사합니다.
'Old_SWIFT(221012) > 기본이야기' 카테고리의 다른 글
멀티 스레딩을 위한 API(GCD Queue) (0) | 2022.09.30 |
---|---|
CGPoint, CGSize, CGRect, Bounds, Frame (0) | 2022.09.28 |
inout(In-Out) 파라미터 (0) | 2022.09.27 |
Oh My Zsh...(feat. Terminal, git bash 한글 -> 영어, Home Brew, 테마(agnoster), 색상변경, iTerm2 설치, 폰트깨짐) (2) | 2022.09.26 |
[Xcode Error]Missing package product 'package name' (1) | 2022.09.15 |