33일차 수업 내용을 정리한 글입니다.
Learned
타입 캐스팅
1) 개요
- 타입 캐스팅이란 인스턴스의 타입을 확인하거나 인스턴스를 같은 계층에 있는
부모 또는 자식 클래스처럼 사용하는 것을 말함. - 인스턴스가 특정 프로토콜을 준수하는지 확인할 수도 있음.
- 타입 캐스팅된 인스턴스는 실제 인스턴스가 갖고 있는 멤버가 변하는 것이 아님.
본래 타입의 인스턴스는 메모리에 그대로 존재하고 특정 멤버에 대한 접근 여부가 변경되는 것임. - 즉, 캐스팅은 실제 인스턴스나 값을 변경하는게 아니라 지정한 타입으로 취급하는 것임.
2) 종류
- is 연산자를 사용해 어떤 타입의 인스턴스인지 확인할 수 있음.
- as 연산자를 사용한 타입 캐스팅은 업 캐스팅과 다운 캐스팅이 있음.
- 업 캐스팅
- as 연산자를 사용해 서브 클래스 타입의 인스턴스를 수퍼 클래스 타입으로 취급할 수 있음.
- 컴파일 시점에 캐스팅 가능 여부를 확인하기 때문에 캐스팅이 불가할 경우 컴파일 에러가 뜸.
- 다운 캐스팅
- 부모 클래스 타입의 인스턴스를 자식 클래스 타입으로 취급하는 것
- as? 를 사용한 다운 캐스팅은 실패 시 nil을 반환하고 성공 시 지정한 타입의 인스턴스를 반환함.
- as!는 실패 시 런타임 오류가 발생한다는 점이 as?와의 차이점이다.
3) Any, AnyObject의 타입 캐스팅
- Any는 클래스, 구조체, 열거형, 함수 타입 등 모든 타입을 나타내고
AnyObject는 클래스 타입만을 나타냄. - Any와 AnyObject는 여러 타입에 대응 할 수 있지만
실제 인스턴스의 타입으로 다운 캐스팅하는 과정이 필요함. - switch-case 구문에 as 연산자를 사용해서 타입 캐스팅이 가능한지 확인하고
캐스팅된 인스턴스를 사용할 수도 있다.
var things:[Any] = [4, 1.5, "hello", { print("this is closure") }, (100, 52.5)]
for thing in things {
switch thing {
case let someInt as Int:
print("이 원소는 정수입니다 : \(someInt)")
case let someDouble as Double where someDouble > 0:
print("이 원소는 양수인 실수입니다 : \(someDouble)")
case let someString as String:
print("이 원소는 문자열입니다 : \(someString)")
case let (x, y) as (Int, Double):
print("이 원소는 튜플입니다 : \(x), \(y)")
case let someClosure as () -> ():
someClosure()
default:
print("something else")
}
}
Generic
1) 개요
- 함수, 타입, 프로토콜에서 타입에 유연한 코드를 작성하게 해주는 요소
- 코드의 중복을 줄이고 재사용성을 높이는 효과가 있음.
프로토콜에서 제네릭을 사용하려면 연관 타입(associated type)을 사용해야 함.
제네릭 함수를 오버로딩해서 사용할 경우 타입이 더 명확한 함수의 우선순위가 더 높음.
2) 타입 파라미터 (Type Parameter)
- 타입 파라미터는 특정 타입이 들어가는 플레이스 홀더 역할임.
- 즉, 어떤 타입인지는 모르지만 특정 타입이 함수, 타입, 프로토콜 내부에서 사용됨을 의미함.
- 파라미터의 이름은 T, Key, Value 처럼 대문자 카멜 케이스를 사용함.
- 여러 개의 타입 파라미터를 사용할 수도 있음.
// 제네릭 함수의 예시
// inout 파라미터로 원본을 전달, T: 타입 파라미터
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let temp = a
a = b
b = temp
}
var someInt = 10
var anotherInt = 5
swapTwoValues(&someInt, &anotherInt)
// someInt = 5, anotherInt = 10
var someString = "hello"
var anotherString = "world"
swapTwoValues(&someString, &anotherString)
// someString = "world", anotherString = "hello"
3) 타입 제약 (Type Constraints)
- 타입 파라미터에 들어올 수 있는 타입에 제약 조건을 설정할 수 있음.
- 제네릭 타입을 특정 프로토콜을 따르거나 특정 클래스를 상속하는 타입으로 명시할 수 있음.
- 예를 들어 Dictionary 타입(구조체)는 Key, Value라는 2개의 타입 파라미터를 가지는데
이때 Key에 들어오는 타입이 hashable 프로토콜을 준수하는 타입이어야만
hash값을 통해 키의 유일성을 보장할 수 있음.
// 배열에서 특정 값의 인덱스를 찾는 함수
func findIndex<T: Equatable>(of valueToFind: T, in array:[T]) -> Int? {
// 값을 비교할 수 있는 타입이어야 하기 때문에 Equatable 프로콜을 준수하는 타입이 들어와야 함.
for (index, value) in array.enumerated() {
if value == valueToFind {
return index
}
}
return nil
}
Learning
- 스위프트 공식 문서의 "Generic" 공부하고 정리하기
'TIL' 카테고리의 다른 글
[Sesac IOS] 36일차 TIL (0) | 2022.08.24 |
---|---|
[Sesac IOS] 34~35일차 TIL (0) | 2022.08.22 |
[Sesac IOS] 32일차 TIL (0) | 2022.08.18 |
[Sesac IOS] 31일차 TIL (0) | 2022.08.17 |
[Sesac IOS] 30일차 TIL (0) | 2022.08.16 |