swift가 컴파일 시 타입을 확인하는 것을 타입 확인이라고 하는데 이를 통해 여러 타입을 섞어 사용할 때 발생할 수 있는 런타임 오류를 피할 수 있다.
기본적으로 제공하는 데이터 타입이든, 사용자가 임의로 만든 데이터 타입이든 이미 존재하는 데이터 타입에 임의로 다른 이름(별칭)을 부여할 수 있다.
typealias MyInt = Int
typealias YourInt = Int
var num1: MyInt = 10
var num2: YourInt = 20
var num3: Int = 30
num1 = num2
num2 = num3
num3 = num1
타입의 이름이 따로 없으며, 프로그래머 마음대로 만드는 타입이다. '지정된 데이터의 묶음'이라고 표현할 수도 있다.
튜플의 각 요소를 접근하려면 각 요소를 이름이 아니라 숫자로 표현하기 때문에 차후에 프로그래머가 코드를 볼 때 불편함이 있을 수 있으므로 튜플의 각 요소마다 이름을 붙여줄 수도 있다.
var person: (name: String, age: Int, height: Double) = ("silverCastle",24,174.5)
print("name: \(person.name)")
print("age: \(person.age)")
print("height: \(person.height)")
결과
name: silverCastle
age: 24
height: 174.5
타입 별칭을 이용해 튜플을 만들 수도 있다.
typealias PersonData = (name: String, age: Int, height: Double)
var person1: PersonData = ("silverCastle",24,174.5)
var person2: PersonData = ("goldCastle",20,170)
print(person1)
print(person2)
결과
(name: "silverCastle", age: 24, height: 174.5)
(name: "goldCastle", age: 20, height: 170.0)
튜플 외에도 많은 수의 데이터를 묶어서 저장하고 관리할 수 있는 컬렉션 타입이 있다. 프로그래머가 이를 잘 숙지한다면 더 효율적으로 프로그래밍할 수 있을 것이다.
같은 타입의 데이터를 일렬로 나열한 후 순서대로 저장하는 형태의 컬렉션 타입이다. 서로 다른 위치에 같은 값이 들어갈 수 있음을 기억하자.
또한 C언어와는 다르게 필요에 따라 자동으로 크기를 조절해주므로 요소의 삽입 및 삭제가 자유롭다.
//var names: Array<String> = ["bronzeCastle","silverCastle","goldCastle"] // 아래와 동일한 표현이다.
var names: [String] = ["bronzeCastle","silverCastle","goldCastle"]
print(names)
names.append("diamondCastle")
print(names)
names.insert("platinumCastle", at: 3)
print(names)
names.insert(contentsOf: ["start","end"], at: 5)
print(names)
names.remove(at: 5)
print(names)
names.removeLast()
print(names)
결과
["bronzeCastle", "silverCastle", "goldCastle"]
["bronzeCastle", "silverCastle", "goldCastle", "diamondCastle"]
["bronzeCastle", "silverCastle", "goldCastle", "platinumCastle", "diamondCastle"]
["bronzeCastle", "silverCastle", "goldCastle", "platinumCastle", "diamondCastle", "start", "end"]
["bronzeCastle", "silverCastle", "goldCastle", "platinumCastle", "diamondCastle", "end"]
["bronzeCastle", "silverCastle", "goldCastle", "platinumCastle", "diamondCastle"]
만약, 접근하려는 인덱스가 인덱서의 범위를 벗어난다면 어떻게 될까?
당연하듯이, 오류를 내뱉게 된다.
print(names[10])
결과
error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).
요소들이 순서와 상관없이 항상 키와 값의 쌍으로 구성되는 컬렉션 타입이다. 하나의 딕셔너리 안에는 같은 키가 중복될 수 없음을 기억하자.
var numberForName: [String: Int] = ["bronzeCastle": 21, "silverCastle": 24, "goldCastle": 25]
print(numberForName)
print(numberForName.count)
print(numberForName["silverCastle"]!)
print(numberForName.removeValue(forKey: "bronzeCastle")!)
print(numberForName["diamondCastle", default: 0])
numberForName["diamondCastle"] = 100
print(numberForName["diamondCastle"]!)
결과
["goldCastle": 25, "bronzeCastle": 21, "silverCastle": 24]
3
24
21
0
100
여기서 출력할 때 !가 붙는 이유는 값이 있을 수도 있고 없을 수도 있기 때문에 옵셔널(Optional)로 정의되어 있는데 키워드인 !를 붙이면 이 옵셔널에 값이 있다고 가정하고 값에 바로 접근할 수 있도록 도와주기 위한 것이다.
만약 키워드 !가 없다면 아래와 같은 오류를 내뱉게 된다.
Expression implicitly coerced from 'Int?' to 'Any'
같은 타입의 데이터를 순서와 상관없이 하나의 묶음으로 저장하는 형태의 컬렉션 타입이다. 세트 내의 값은 모두 유일한 값으로 중복될 수 없음을 기억하자.
그래서 세트는 보통 순서가 중요하지 않거나 각 요소가 유일한 값이어야 하는 경우에 사용한다.
배열과 달리 축약형(ex. Array[Int]를 [Int]로 축약)이 없다.
// 순서가 없기 때문에 매 출력마다 값이 다르게 나올 수 있다.
var names: Set<String> = ["bronzeCastle","silverCastle","goldCastle","silverCastle"]
print(names)
names.insert("diamondCastle")
print(names)
결과
["bronzeCastle", "silverCastle", "goldCastle"]
["bronzeCastle", "diamondCastle", "silverCastle", "goldCastle"]
연관된 항목들을 묶어서 표현할 수 있는 타입이다. 다음과 같은 경우에 요긴하게 사용할 수 있다.
열거형은 enum 키워드를 통해 선언할 수 있다.
enum School {
case primary, elementary, middle, high, college, university, graduate
}
let highSchoolStudent: School = School.high
열거형의 각 항목은 자체로도 하나의 값이지만 항목의 원시값(Raw Value)도 가질 수 있다. 특정 타입의 값을 원시 값으로 가지고 싶다면 열거형 이름 오른쪽에 타입을 명시해주면 된다.
enum School: String {
case primary = "유치원"
case elementary = "초등학교"
case middle = "중학교"
case high = "고등학교"
case college = "대학"
case university = "대학교"
case graduate = "대학원"
}
let highSchoolStudent: School = School.high
// 원시 값을 사용하고 싶다면 rawValue 프로퍼티를 이용하자.
print("저는 \(highSchoolStudent.rawValue) 학생입니다.")
결과
저는 고등학교 학생입니다.
각 항목 옆에 소괄호로 묶어 표현할 수 있다.
enum MainDish {
case pasta(taste: String)
case pizza(dough: String, topping: String)
case chicken(withSauce: Bool)
case rice
}
var dinner: MainDish = MainDish.pasta(taste: "크림")
print(dinner)
dinner = .pizza(dough: "치즈크러스트", topping: "불고기")
print(dinner)
dinner = .chicken(withSauce: true)
print(dinner)
dinner = .rice
print(dinner)
결과
pasta(taste: "크림")
pizza(dough: "치즈크러스트", topping: "불고기")
chicken(withSauce: true)
rice
열거형에 포함된 모든 케이스를 알고 싶을 때 사용한다. 열거형의 이름 뒤에 콜론(:)을 작성하고 한칸 띄운 뒤 CaseIterable 프로토콜을 적으면 된다.
enum School: CaseIterable {
case primary, elementary, middle, high, college, university, graduate
}
// 모든 케이스의 컬렉션을 생성하려면 allCases 프로퍼티를 이용하자.
let allCases: [School] = School.allCases
print(allCases)
결과
[__lldb_expr_56.School.primary, __lldb_expr_56.School.elementary, __lldb_expr_56.School.middle, __lldb_expr_56.School.high, __lldb_expr_56.School.college, __lldb_expr_56.School.university, __lldb_expr_56.School.graduate]
열거형 항목의 연관 값이 열거형 자신의 값이고자 할 때 사용한다. indirect 키워드를 사용하면 되는데 특정 항목에만 적용하고 싶다면 case 키워드 앞에 indirect를 붙이면 되고, 열거형 전체에 적용하고 싶다면 enum 키워드 앞에 indirect를 붙이면 된다.
enum ArithmeticExpression {
case number(Int)
indirect case addition(ArithmeticExpression, ArithmeticExpression)
indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
}
indirect enum ArithmeticExpression {
case number(Int)
case addition(ArithmeticExpression, ArithmeticExpression)
case multiplication(ArithmeticExpression, ArithmeticExpression)
}
열거형의 각 케이스를 비교하고 싶을 때 사용한다. 열거형의 이름 뒤에 콜론(:)을 작성하고 한칸 띄운 뒤 Comparable 프로토콜을 적으면 된다.
참고로, 앞에 위치한 케이스가 더 작은 값을 가진다.
enum Condition: Comparable {
case bad
case good
case great
}
let myCondition: Condition = Condition.great
let yourCondition: Condition = Condition.bad
if myCondition >= yourCondition {
print("제 상태가 더 좋군요.")
}
else {
print("당신의 상태가 더 좋군요.")
}
결과
제 상태가 더 좋군요.