Bool object 타입(struct)은 true
와 false
두 가지 값만을 가진다.
Cocoa 메소드에서 Bool 파라미터를 사용하거나 Bool 값을 리턴하는 경우가 많다.
!
연산자를 사용하면 Bool 값을 반전시킬 수 있다.
v.isUserInteractionEnabled = !v.isUserInteractionEnabled
변수의 값을 불러온 후 !
를 이용해서 값을 반전시키고 그 값을 변수에 다시 할당한다.
Bool 변수의 toggle
메소드를 호출하면 더 간단하게 값을 반전시킬 수 있다.
v.isUserInteractionEnabled.toggle()
주요한 숫자 타입에는 Int와 Double이 있다. 즉, iOS 디바이스에서는 두 타입이 일반적으로 사용된다. 다른 숫자 타입은 주로 iOS 프로그래밍을 위해 필요한 C와 Objective-C API의 호환성을 위해 존재한다.
Int object 타입은 Int.min
과 Int.max
사이의 정수를 표현한다. 이 최솟값과 최대값은 플랫폼과 앱이 실행되는 아키텍처에 따라 다르다.
Int 값을 표현하는 가장 쉬운 방법은 숫자 리터럴을 사용하는 것이다. 정수 리터럴은 기본적으로 Int로 인식된다.
Int 리터럴을 2진수, 8진수, 16진수로 표현하는 것도 가능하다. 이를 위해서는 숫자 앞에 0b
, 0o
, 0x
를 붙여주어야 한다. 예를 들어, 0x10
은 16(10)을 의미한다.
Double object 타입은 소숫점 약 15자리 이하(64비트 기준)까지의 부동 소수점 숫자를 나타낸다.
Double 값을 표현하는 가장 쉬운 방법 역시 숫자 리터럴을 사용하는 것이다. 소숫점을 포함한 숫자 리터럴은 기본적으로 Double로 인식된다.
C나 Objective-C와 달리 Double 리터럴은 소숫점으로 시작할 수 없다.
Double 리터럴을 과학적 표기법으로 표기하는 것도 가능하다. e
뒤에 나오는 숫자는 10의 지수를 의미하며, 소숫점은 생략해도 된다. (예: 3e2
== 300)
Double 리터럴을 16진수로도 표현할 수 있다. 이때 리터럴 앞에 0x
를 붙여주어야 한다. 여기서도 거듭제곱법을 사용할 수 있다. p
뒤에 오는 숫자는 2의 지수를 의미한다. 역시 소숫점을 생략해도 된다. (예: 0x10p2
== 64)
Double.infinity
, Double.pi
와 같은 static 프로퍼티와 isZero
등의 인스턴스 프로퍼티가 있다.
Coercion이란 값의 타입이 다른 타입으로 변환되는 것을 의미하며, Numeric coercion이란 숫자 타입의 값이 다른 숫자 타입으로 변환되는 것을 말한다.
스위프트에는 명시적으로 형변환을 하는 방법이 없지만 대신 인스턴스화를 통해서 형변환을 할 수 있다. 스위프트의 숫자 타입들은 다른 숫자 타입을 파라미터로 갖는 생성자를 가지고 있다.
let i = 10
let x = Double(i)
print(x) // 10.0
let y = 3.8
let j = Int(y)
print(j) // 3
Cocoa는 다양한 숫자 타입들이 있기 때문에 스위프트는 이런 타입들에 대응하는 타입들을 가지고 있다.
Int는 Int8, Int16, Int32, Int64 같은 다양한 signed integer 타입과 UInt, UInt8, UInt16, UInt32, UInt64 같은 unsigned integer 타입이 있다. 또한 Double뿐만 아니라 Float, Float16, Float8o, CGFloat이 있다.
C API를 사용하기 위해서는 C 숫자 타입을 사용해야 한다. 이런 타입들은 단지 type alias이다. 예를 들어 CDouble은 Double의 다른 이름이고, CLong은 Int의 다른 이름이다. Cocoa 프레임워크에도 다양한 숫자 type alias들이 있다. TimeInterval(Objective-C NSTimeInterval)은 Double의 type alias이다.
모든 숫자 값들이 다른 타입으로 변환되지 않는다. 다양한 크기의 정수들이 다른 정수 타입의 범위를 벗어날 수 있다. 예를 들어, Int8.max
은 127로 128 이상의 값은 할당할 수 없다.
let i: Int16 = 128
let ii = Int8(i)
위 코드는 런타임에서 에러가 발생한다. 에러를 막는 방법 중 하나는 exactly:
이니셜라이저를 사용하는 것이다. 이는 failable 이니셜라이저로, 에러는 발생하지 않지만 형변환이 이루어졌는지 테스트하는 코드를 추가해야 한다.
let i: Int16 = 128
let ii = Int8(exactly: i)
if // ... need to test
또 다른 방법은 clamping:
이니셜라이저를 사용하는 것이다. 이 이니셜라이저는 범위를 벗어난 값을 버리기 때문에 반드시 형변환에 성공한다.
let i: Int16 = 128
let ii = Int8(clamping: i) // 127
정수 오버플로우나 언더플로우는 런타임 에러를 발생시킨다. 그러나 오버플로우나 언더플로우 연산을 강제하는 특별한 메소드가 존재한다.
let i = Int.max - 2
let (j, over) = i.addingReportingOverflow(12/2) // (Int.min + 3, true)
addingReportingOverflow
는 튜플을 반환하는데, 첫 번째 값은 오버플로우가 발생된 결과값이고 두 번째 값은 오버플로우의 발생 여부(Bool)이다.
오버플로우/언더플로우 발생 여부가 필요 없다면 &+
, &-
, &*
연산자를 사용할 수 있다.
숫자는 비교 연산자를 이용하여 값을 비교할 수 있다. 이때 비교 연산자는 Bool을 리턴한다.
산술 연산과 다르게 비교 연산에서는 다른 타입을 가진 정수 값을 비교할 수 있다.
let i: Int = 1
let i2: UInt8 = 2
let ok = i < 12 // true
let ok2 = i == 12 // false
let sum = i + 12 // error
그러나 컴퓨터가 숫자를 저장하는 방식 때문에 Double 값은 우리가 예상하는 것과 다른 비교 연산의 결과가 나올 수도 있다. 예를 들어, 0
에 0.1
을 10번 더하는 것은 0.1
에 10
을 곱하는 것과 다른 결과가 나온다.
let f = 0.1
var sum = 0.0
for _ in 0..<10 { sum += f }
let product = f * 10
let ok = sum == product // false
대신 두 값이 서로 가까운지 확인하는 방법을 사용할 수 있다.
let ok = sum >= product.nextDown && sum <= product.nextUp // true