저번 포스팅에서 우리는 associatedtype을 통해 프로토콜안에서 범용 타입을 편하게 사용하는 방법을 학습했다.
이번 학습은 associatedtype을 사용할 때 발생하는 문제점과 해결방안을 알아보자.
문제점
associatedtype이 정의된 protocol은 리턴 또는 변수 타입으로 사용될 수 없는 불편함이 있다.
protocol SomeProtocol {
associatedtype T : Equatable // 제약 조건 도 줄 수 있음
}
protocol SomeProtocol2 {}
var prop: SomeProtocol // ❌ associatedtype가 정의되서 변수 타입으로 사용 불가
func someFunc() -> SomeProtocol {} // ❌ associatedtype가 함수 리턴 타입으로 사용 불가
var prop2: SomeProtocol2 // ✅ associatedtype가 없어 변수 타입으로 사용 가능
func someFunc2() -> SomeProtocol2 {} // ✅ associatedtype가 없어 함수 리턴 타입으로 사용 가능
any
탄생 배경
다양한 타입의 값을 동적으로 처리해야 하는 경우에 any를 사용하여 해당 값을 처리하거나
다른 타입으로 변환할 수 있다. 대표적인 예로는 여러 가지 다양한 타입의 값을 하나의 컬렉션에
저장할 때나, 함수의 인자로 다양한 타입의 값을 받아서 처리할 때가 있을 수 있다.
특징
1) any는 동적으로 타입을 저장하고 런타임 시에 확인하는 특징이 있다.
2) associatedtype이 정의되어도 property 타입, return 타입, 매개변수 타입으로 사용가능하다.
3) existential type이다. = 해당 프로토콜을 따르는 type
4) 프로퍼티로 사용 시 생성자를 통해 초기화가 가능하며, 프로퍼티 선언과 할당을 별도로 할 수 있다.
5) 특정 타입으로 추상화하는 역할을 한다.
some
탄생 배경
두번 째 학습할 키워드는 some이며 some where의 단점을 보완하기위해 등장했다.
where는 특정 패턴 또는 특정 제약조건을 만족하는 범위로 줄이는 역할을 한다.
some 특정 조건을 만족하는 범위로 줄이는 역할을 한다.
특징
1) associatedtype이 정의되어도 property 타입, return 타입, 매개변수 타입 사용가능하다.
2) underlying type이다. = Opaque type(불투명한 타입)으로 추상화 되기 전의 구체적인 타입
3) 프로퍼티로 사용 시 생성자를 통해 초기화 받을 수 없음, 변수 선언과 동시에 할당해야한다.
4) 특정 타입으로 제한하는 역할을 한다.
Opaque type
opaque type은 말그대로 불투명한 타입으로 해석되며 추상화된 타입이라고 생각된다.
실질적인 구현은 some + protocol을 통해 구현한다.
자주 비교되는 대상이 generic 타입이다. 한번 두 타입을 비교해보자.
Generic | Opaque type | |
구현부 | 추상화 하여 작성 | 구체적인 타입 지정 (타입이 명확해짐) |
호출부 | 구체적인 타입 지정 (타입이 명확해짐) | 추상화 하여 작성 |
Generic vs Opaque type
some과 any 구분하기
any와 some은 역할이 분명하다 any는 추상화 , some은 특정 타입으로 제한하다.
그림 자료와 함께 다시 살펴보자.
some은 [Animal] 이라고 되어있지만 모두 1가지 타입(Cow)으로 제한되어 있 형태이고
any는 [Animal]에 여러가지 Animal 프로토콜을 채택하고있는 동물 구현체들이 저장되어 있는 것을 볼 수 있다.
이번에는 코드를 통해 차이점을 비교해보자.
protocol OS {}
struct iOS: OS {}
struct Android: OS {}
var some1 : some OS = Android() // ✅
some1 = iOS() // ❌ 이후 다른 타입 할당 불가
var some2 : [some OS] = [iOS(),iOS()] // ✅ 같은 타입은 할당 가능
var some3 : [some OS] = [] // ❌ 비어있을 수 없다
some3 = [Android()] // ❌ // 이후 다른 타입 할당 불가
var some4 : [some OS] = [iOS(),Android()] // ✅ 다른 타입 할당 불가
var any1 : any OS = Android() // ✅
any1 = iOS() // ✅ 이후 다른 타입 할당 가능
var any2 : [any OS] = [iOS(),iOS()] // ✅ 같은 타입은 할당 가능
var any3 : [any OS] = [] // ✅ 비어잇을 수 있다.
any3 = [Android()] // ✅ 이후 다른 타입 할당 가능
var any4 : [any OS] = [iOS(),Android()] // ✅ 다른 타입 할당 가능
이번에는 두개를 한번 역할에 맞게 섞어 써보자.
protocol OS {
var name: String { get }
}
struct iOS: OS { let name: String = "iOS" }
struct Android: OS { let name: String = "Android" }
struct Inspector {
func inspec(_ os: some OS) { // 특정 OS로 타입을 제한하여 사용한다.
print("검사 완료 \(os.name)")
}
func insepctAll(_ oss: [any OS]) { // OS 구분없이 여러 OS를 한번에 받아 드릴 때
for os in oss {
inspec(os)
}
}
}
var incs = Inspector()
incs.insepctAll([iOS(), Android()])
추상화된 형태(any)로 한번에 여러개를 받은 후 특정 제한(some)으로 처리할 때 이렇게 엮어서 사용할 수 있다.
정리
카드 가독성과 편의성을 높여주는 any와 some이라는 좋은 키워드를 학습했다.
아직 100% 완벽하게 두 키워드를 알맞은 상황에서 구분하여 사용할 수는 없지만 개념적으로는 많은 성장을 이뤄낸 것 같다.
'프로그래밍언어 > swift' 카테고리의 다른 글
property 학습하기 (2) [ computed Property ] (0) | 2024.08.25 |
---|---|
property 학습하기 (1) [ Stored Property ] (0) | 2024.08.25 |
protocol 학습하기 (3) [ associatedtype ] (0) | 2024.08.25 |
protocol 학습하기 (2) [ generic, 합성, 채택 체크 ] (0) | 2024.08.25 |
protocol 학습하기 (1) [ property, method, extension ] (0) | 2024.08.24 |