TIL

[Sesac IOS] 33일차 TIL

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