[CH4] 데이터 타입 고급 - 데이터 타입 안심

Tabber·2021년 9월 7일
0
post-thumbnail

4.1 데이터 타입 안심

스위프트는 안정성(Safe)이 가장 뚜렷하게 나타나는 언어이다.

애플이 처음 스위프트를 발표했을 때 강조했던것이 안정성이다.

스위프트의 상수는 코드를 안정되고 깔끔하게 만들 수 있도록 사용된다.

서로 다른 타입끼리 데이터 교환은 꼭 타입 캐스팅을 거쳐야 한다.

import UIKit

var greeting = "Hello, playground"

let a = 12

let plus = greeting + String(a) //형변환하지 않을 시 에러

print(plus)

스위프트에서 값 타입의 데이터 교환은 엄밀히 말하면 타입 캐스팅이 아닌, 새로운 인스턴스를 생성하여 할당하는 것이다.

Swift에서의 타입 캐스팅

스위프트는 데이터 타입 안전을 위해 각기 다른 타입끼리의 값 교환엄격히 제한한다.
또한, 다른 프로그래밍 언어에서 지원하는 암시적 데이터 타입 변환(Implicit Type Conversion)은 지원하지 않는다.


암시적 데이터 타입 변환이란?

C언어에서 다른 타입끼리의 연산은 우선, 피연산자들을 모두 같은 타입으로 만든 후에 수행한다.
이렇게 하나의 타입을 다른 타입으로 바꾸는 행위를 타입 변환(Type Conversion)이라고 한다.

암시적 타입 변환은 대입 연산이나 산순 연산에서 컴파일러가 자동으로 실행해주는 타입 변환을 가리킨다.
대입 연산 시 연산자의 오른쪽에 존재하는 데이터의 타입이 연산자의 왼쪽에 존재하는 데이터의 타입으로 묵시적 타입 변환이 진행된다.

char ch = 200;
int num01 = 3.14;
double num02 = 5;

printf("ch에 저장된 값은 %d이다", ch);
printf("num01에 저장된 값은 %d이다", num01);
printf("num02에 저장된 값은 %f이다", num02);

// ch에 저장된 값은 -56이다
// num01에 저장된 값은 3이다
// num02에 저장된 값은 5.000000이다

이 예제에서는 char형 변수에 char형 변수가 표현할 수 있는 범위를 넘는 데이터를 저장한다.
따라서 전달된 데이터의 상위 비트가 자동으로 삭제되어 데이터의 손실이 발생한다.

또한, int형 변수에 실수를 저장했기 때문에 소수부분이 자동으로 삭제되어 데이터의 손실이 발생한다.
하지만, double형 변수에 int형 데이터를 저장하는 것은 데이터가 double형으로 자동 타입 변환되지만, 데이터의 손실은 발생하지 않는다.


위의 암시적 데이터 타입 변환을 보게되면 기존에 가지고 있던 데이터를 손실당할 수 있는 변환이기 때문에 안정성을 중요로 생각하는 스위프트에서는 지원하지 않는 것 이다.

또한, C언어의 경우 데이터 타입 변환을 통해 손쉽게 다른 데이터 타입으로의 변환이 가능하다.
예를 들어 int num = (int)2.3f; 와 같이 표현하거나 int num = 2.3f 처럼 표현하여 데이터 타입을 변환할 수 있었다.

스위프트에서는 Int(2.3) 처럼 부동소수 타입을 정수 타입의 값으로 변환해주는 형태와 C언어의 데이터 타입 변환 형태는 크게 다를게 없어 보이지만, 사실은 좀 다르다.

먼저 C언어와 스위프트의 데이터 타입 변환을 살펴보겠다.

// C 언어
double value = 3.3
int convertedValue = (int)value 
convertedValue = 5.5 // double - > int 암시적 데이터 타입 변환
// 스위프트
var value: Double = 3.3 
var convertedValue: Int = Int(value) 
convertedValue = 5.5 // 오류 !!

C 언어 코드를 살펴보면 부동소수 타입인 double에서 정수 타입인 int로 데이터 타입을 변경했다.

C언어에서는 이를 타입 변환이라고 칭한다.

그러나 스위프트 코드를 보면, Int(value) 라는 형태로 데이터 타입의 형태를 변경해주는데, 이는 이니셜라이저 이다. 즉, 기존 값을 전달인자로 받는 이니셜라이저를 통해 새로운 Int 구조체의 인스턴스를 생성한다.

스위프트에서는 이를 타입 변환 혹은 타입캐스팅이라고 칭하지 않는다.
다만, 이니셜라이저를 통해 새로운 인스턴스를 생성하는 과정이다. 그래서 Int 구조체의 정의를 들여다 보면 다양한 이니셜라이저가 정의되어 있음을 확인할 수 있다.

Int 구조체를 들여다보면, 설명이 이렇게 쓰여있다.

Creates a new instance with the same memory representation as the given value.
(지정된 값과 동일한 메모리 표현을 사용하여 새 인스턴스를 만듭니다)

아까 위에서 설명했듯이, 단순한 형 변환이 아니라, 이니셜라이저를 이용한 새로운 인스턴스를 만듦으로써 형 변환과 같은 작업을 수행하는 것이다.

Int의 이니셜라이저는 대부분 실패하지 않는 이니셜라이저로 정의되어 있다.
그러나, 좀 더 살펴보면 실패가 가능한 이니셜라이저도 포함되어있다. StringProtocol 타입을 매개변수로 받는 @inlinable public init?<S>(_ text: S, radix: Int = 10) where S : StringProtocol 등이 실패 가능한 이니셜라이저이다.

@inlinable public init?<S>(_ text: S, radix: Int = 10) where S : StringProtocol 의 코드를 예시로 조금 훑어보면,

If description is in an invalid format, or if the value it denotes in base 10 is not representable, the result is nil. For example, the following conversions result in nil:

  • Int(" 100") // Includes whitespace
  • Int("21-50") // Invalid format
  • Int("ff6600") // Characters out of bounds
  • Int("10000000000000000000000000") // Out of range

description 의 형식이 잘못되었거나, description 이 나타내는 값이 잘못된 경우 Base 10 을 나타낼 수 없다. 결과는 nil 이 발생합니다.

  • 공백포함
  • 잘못된 형식
  • 문자가 범위를 벗어남
  • Int의 범위를 벗어남

StringProtocol 타입의 데이터 text를 Int 타입으로 변경할 때, 적절하지 못한 매개변수가 전달된다면 새로운 인스턴스가 생성되지 않을수도 있다라는 뜻이다.

이렇게 스위프트는 데이터의 타입 변경이 아닌 새로운 인스턴스를 생성해 변환하는 것처럼 할당하는 것이다.


4.1 .1 데이터 타입 안심이란

스위프트는 앞서 말했듯이 데이터 타입을 안심하고 사용할 수 있는(Type-Safe) 언어이다.
타입을 안심하고 사용할 수 있다는 말은 그만큼 실수를 줄일 수 있다는 의미이다.
예를 들어 Int 타입 변수에 할당하려는 값이 Character 타입이라면 컴파일 오류가 발생한다.

이런 오류는 프로그래밍 도중에 눈치채기가 어려워서 컴파일러가 알려주지 않으면 나중에 오류를 찾아내기도 쉽지 않다.

그렇지만 스위프트는 컴파일 오류로 알려주므로 서로 다른 타입의 값을 할당하는 실수를 줄일 수 있다.
이렇게 스위프트가 컴파일 시 타입을 확인하는 것을 타입 확인 이라고 한다.


타입확인이란?

타입 확인 연산자인 is 를 사용하여 인스턴스가 어떤 클래스(혹은 어떤 클래스의 자식클래스)의 인스턴스인지 타입을 확인할 수 있다. 타입 확인 연산자는 인스턴스가 해당 클래스의 인스턴스를 그 자식클래스의 인스턴스라면 true 를 반환하고, 그렇지 않으면 false 를 반환한다.

is 연산자 클래스의 인스턴스뿐만 아니라 모든 데이터 타입에 사용이 가능하다.

class Coffee {
    let name: String
    let shot: Int
    
    var description: String {
        return "\(shot) shot(s) \(name)"
    }
    
    init(shot: Int) {
        self.shot = shot
        self.name = "coffee"
    }
}

class Latte: Coffee {
    var flavor: String
    
    override var description: String {
        return "\(shot) shot(s) \(flavor) latte"
    }
    
    init(flavor: String, shot: Int) {
        self.flavor = flavor
        super.init(shot: shot)
    }
}

class Americano: Coffee {
    let iced: Bool
    
    override var description: String {
        return "\(shot) shot(s) \(iced ? "iced": "hot") americano"
    }
    
    init(shot: Int, iced: Bool) {
        self.iced = iced
        super.init(shot: shot)
    }
}

let coffee: Coffee = Coffee(shot: 1)
print(coffee.description) // 1 shot(s) coffee

let myCoffee: Americano = Americano(shot: 2, iced: false)
print(myCoffee.description) // 2 shot(s) hot americano

let yourCoffee: Latte = Latte(flavor: "green tea", shot: 3)
print(yourCoffee.description) // 3 shot(s) green tea latte

print(coffee is Coffee) // ture
print(coffee is Americano) // false
print(coffee is Latte) // false

print(myCoffee is Coffee) // true
print(yourCoffee is Coffee) // true

print(myCoffee is Latte) // false
print(yourCoffee is Latte) // true

코드를 보면 coffee 인스턴스는 Coffee 타입이다. 따라서 coffee는 Latte 타입이나 Americano 타입이 될 수 없다.

그러나, myCoffee는 Americano 타입이고, yourCoffee는 Latte 타입이므로 myCoffee와 yourCoffee는 Coffee 타입인지 확인했을때 true 를 반환한다.

하지만, myCoffee 와 yourCoffee는 서로 특성이 다르고 부모와 자식 클래스의 관계도 아니기에 서로 다른 타입이다. 따라서 myCoffee는 Latte 타입이 될 수 없기에 false 를 반환하게 된다.

is 연산자 외에도 타입을 확인해 볼 수 있는 방법이 존재한다. 바로 메타 타입 타입 인데, 이 부분은 추후에 19장을 공부할 때 알아보겠다.

따라서 타입확인은 스위프트가 컴파일 시 타입의 true, false를 알려주는 것이다.

타입확인을 통해 여러 타입을 섞어 사용할 때 발생할 수 있는 런타임 오류를 피할 수 있게 된다.


4.1.2 타입 추론

스위프트에서는 변수나 상수를 선언할 때 특정 타입을 명시하지 않아도 컴파일러가 할당된 값을 기준으로 변수나 상수의 타입을 결정한다.

var name = "ITlearning" // 타입 미지정

name = 100 // 오류 발생

예를 들어 var name = "ITlearning" 라는 코드를 작성하면, 컴파일러가 컴파일을 하면서 name의 타입을 String 타입으로 결정하게 된다. 따라서 name 은 String 타입이기 때문에, 아래 코드처럼 다른 타입의 데이터를 넣을 경우 오류가 발생하게 된다.

4.1 단원 공부를 해보았다.

간단 정리

  • 스위프트라는 언어는 안정성을 가장 중요시하는 언어이다. 따라서 타입 변환이 엄격하다.
  • 서로 다른 데이터의 교환을 하고 싶다면 꼭 타입 캐스팅을 거쳐야 한다.
  • 스위프트에서의 타입 캐스팅은 엄밀히 말하면 타입 캐스팅이 아니라 새로운 인스턴스 생성이다.
  • 데이터 타입 안심이란 타입을 안심하고 사용할 수 있다라는 뜻이다.
  • 스위프트는 컴파일 시 타입을 확인하는 것을 타입 확인 이라고 한다.
  • 타입확인을 통해 런타임 오류를 피할 수 있다.
  • 타입 추론이란 변수나 상수를 선언할 때 타입을 선언하지 않아도 컴파일러가 할당된 값을 기준으로 타입을 결정한다.
  • 만일 String 타입의 데이터를 넣었다면 타입은 String으로 결정된다.
  • 따라서 한번 결정된 타입으로 인해 다른 타입의 데이터를 넣었을 경우, 오류가 발생한다.
  • 타입 확인 키워드는 is메타 타입 타입이 있다.
profile
iOS 정복중인 Tabber 입니다.

0개의 댓글