본 포스팅은 '스위프트 프로그래밍 (3판) - 야곰 저' 도서와 앨런 Swift 문법 마스터스쿨 강의를 통해 공부하며 정리하는 포스팅입니다.
혹시 틀린 부분이 있거나 질문이 있으시다면 언제든지 댓글 달아주시면 정말 감사하겠습니다 :)
1. 튜플
지정된 데이터의 묶음으로, 연관된 데이터를 같이 묶음으로써 표현할 때 유용함 (ex. 홍길동, 20세, 서울)
- 튜플.인덱스, 튜플.요소이름 으로 튜플 안에서 각 데이터를 꺼내오고, 할당도 가능함
//1. 인덱스로 가져오는 방법
var person: (String, Int, Double) = ("yuri", 10, 170.0)
print("\(person.0), \(person.1), \(person.2)") // yuri, 10, 170
//2. 요소의 이름을 정해주는 방법 (가독성이 좋아짐)
var person: (name: String, age: Int, height: Double) = ("yuri", 10, 170.0)
var person = (name: "yuri", age: 10, height: 170.0) // 타입 추론
print("\(person.name), \(person.age), \(person.height)") //yuri, 10, 170.0
print("\(person.0), \(person.1), \(person.2)") // yuri, 10, 170.0 (마찬가지로 인덱스로 접근 가능)
person.name = "zoe"
person.height = 160.0
- 튜플 분해 (튜플의 각 요소를 상수/변수화 -> 값 바인딩)
let (name, age, address) = ("조이", 20, "서울")
print(name) //조이
- 튜플 별칭
typealias Person = (name: String, age: Int, height: Double)
let yuri:Person = ("yuri", 20, 170.0)
print("이름: \(yuri.name) 나이: \(yuri.age))
- 튜플과 if/switch 문의 활용
let iOS = (language: "Swift", type: "Mobile")
if iOS == ("Swift, "Mobile") { //튜플 채로 비교 가능
}
switch iOS {
case ("Swift", "Mobile"):
print("iOS는 모바일 분야입니다.")
case ("Swift", "Server"):
print("iOS는 서버 분야입니다.")
default:
break
}
//iOS는 모바일 분야입니다.
- switch문을 활용한 튜플 케이스 처리
var coord = (0, 5) // 좌표
switch coord {
case (let distance, 0), (0, let distance): // x축 or y축에 있으면 출력하라는 코드
print("X 또는 Y축 위에 위치하며, \(distance)만큼의 거리가 떨어져 있음")
default:
print("축 위에 있지 않음")
}
switch coord {
case (let x, let y) where x == y: //상수 x, y에 coord 값을 바인딩한 후 where 절로 비교
print("(\(x), \(y))의 좌표는 y = x 1차함수의 그래프 위에 있다.")
case let (x, y) where x == -y:
print("(\(x), \(y))의 좌표는 y = -x 1차함수의 그래프 위에 있다.")
case let (x, y):
print("(\(x), \(y))의 좌표는 일차 함수가 아닌 임의의 지점에 있다.")
}
switch coord {
case (0, 0):
print("(0, 0)은 원점 위에 있다.")
case (-2...2, -2...2): //범위 연산자로 튜플이 해당 범위에 속하는지 확인 가능
print("(\(coord.0), \(coord.1))은 원점의 주위에 있다.")
default:
print("점은 (\(coord.0), \(coord.1))에 위치한다.")
}
2. 콜렉션
- 배열, 딕셔너리, 세트가 있음
- 데이터를 묶어서 효율적으로 관리하기 위한 자료형
배열 (Array)
- 데이터를 순서대로 저장하는 컬렉션 타입 (중복 O)
let array: [Int] = []
let array: Array<Int> = []
let array = Array<Int>()
let array = [Int]()
배열 주요 메소드
원래 외울 필요까지는 없지만
코딩테스트에서 배열/문자열 모르면 절대 안되기 때문에! 계속 새로운걸 알 때마다 갱신해두는 포스팅이 있다.
딕셔너리 (Dictionary)
- 순서 없이 키와 값의 쌍으로 관리하는 컬렉션 타입
// 단축문법
var words: [String: String] = [:]
// 정식문법
var words: Dictionary<String, String>
// 빈 딕셔너리 생성
let emptyDic1: Dictionary<Int, String> = [:]
let emptyDic2 = Dictionary<Int, String>()
let emptyDic3 = [Int: String]()
let emptyDic4:[Int:String] = [:]
- 키 값은 반드시 유일해야하며, Hashable 해야한다. Hashable 한 키값으로 인해 검색속도가 빠르다. (시간복잡도 O(1))
❓Hashable이란?
- 해시함수는 input을 특정 해싱 알고리즘을 적용하여 유일한 값으로 만들어주는 함수인데, 이 때 해시함수의 input으로 쓰일 수 있는 타입은 Hashable 프로토콜을 따르게 된다.
- Swift의 모든 데이터 타입은 Hashable 프로토콜을 채택하고 있어, 해시함수의 input 값 즉 딕셔너리의 key로 사용될 수 있다.
딕셔너리 주요 메소드
원래 외울 필요까지는 없지만
코딩테스트에서 배열/문자열 모르면 절대 안되기 때문에! 계속 새로운걸 알 때마다 따로 갱신해두는 포스팅이 있다.
세트
- 순서와 중복 없는 컬렉션 타입
- 세트 생성시, 배열과 동일하게 대괄호 ([])를 사용함
- 배열과 대부분의 메소드가 유사하고, 다른점은 추가는 insert(_ element) 함수라는 점, 제거는 인덱스가 아닌 remove("삭제하려는 값")이라는 점이 있다.
- Set의 요소로 Hashable 한 (Hashable 프로토콜을 따르는) 데이터 타입의 값이 와야함 (딕셔너리와 동일하게 Hashing 알고리즘이 탐색에 적용되기 때문에 시간복잡도가 O(1) 이다.)
var names: Set<String> = [] // 빈 세트 생성
var names: Set<String> = ["yuri", "yuri", "chulsoo"] // ["yuri", "chulsoo"]
- 집합 연산 (합집합, 교집합, 차집합)
let englishClassStudents: Set<String> = ["john", "chulsoo", "yagom"]
let koreanClassStudents: Set<String> = ["jenny", "yagom", "chulsoo", "hana", "minsoo"]
let intersectSet: Set<String> = englishClassStudents.intersection(koreanClassStudents)
// 교집합 {"yagom", "chulsoo"}
let unionSet: Set<String> = englishClassStudents.union(koreanClassStudents)
// 합집합 {"minsoo", "jenny", "john", "yagom", "chulsoo", "hana"}
let subtractSet: Set<String> = englishClassStudents.subtracting(koreanClassStudents)
// 차집합 {"john"}
print(unionSet.sorted()) // ["chulsoo", "hana", "jenny", "john", "minsoo", "yagom"]
// 코드 4-13 세트의 활용 – 포함관계 연산
let 새: Set<String> = ["비둘기", "닭", "기러기"]
let 포유류: Set<String> = ["사자", "호랑이", "곰"]
let 동물: Set<String> = 새.union(포유류) // 새와 포유류의 합집합
print(새.isSubset(of: 동물)) // 새가 동물의 부분집합인가요? - true
print(동물.isSuperset(of: 포유류)) // 동물은 포유류의 전체집합인가요? - true
print(동물.isSuperset(of: 새)) // 동물은 새의 전체집합인가요? - true
3. 열거형
- 연관된 항목들을 묶어서 표현할 수 있는 타입
- 1) 타입에 제한된 선택지를 주고싶을 때 2) 예상된 입력 값이 한정되어 있을 때
- 각 열거형이 고유의 타입으로 인정되므로, 코드의 가독성과 안정성이 높아짐
1) 기본 열거형
enum School { // 타입이름 : 대문자로 시작
case primary // 각 케이스 : 소문자로 시작
case elementary
case middle
case high
case college
case university
case graduate
}
//한줄로도 표현 가능
enum School { case primary, elementary, middle, high, college, university, graduate }
var highestEducationLevel:School = School.graudate
//위와 같은 표현
var highestEducationLevel:School = .graduate
highestEducationLevel = .primary //위에서 타입 지정을 해주었기 때문에 타입 생략 가능
* switch를 활용한 기본 열거형의 사용
let 최종학력: School = School.university
switch 최종학력 {
case .primary:
print("유치원")
case .elementary:
print("초등학교")
case .middle:
print("중학교")
case .high:
print("고등학교")
case .university, .college: // switch에서 콤마는 or
print("대학교")
case .graduate:
print("대학원")
}
// "대학교"
2) 원시값(rawValue)을 가지는 열거형
- 열거형의 각 항목은 값을 가질 수 있다. 이를 원시값이라 하고, rawValue 프로퍼티를 통해 값을 꺼내올 수 있다.
enum School: String {
case primary = "유치원"
case elementary = "초등학교"
case middle = "중학교"
case high = "고등학교"
case college
case university
case graduate
}
var highestEducationLevel:School = School.university
print("저의 최종 학력은 \(highestEducationLevel.rawValue) 입니다") //저의 최종학력은 university 입니다.
enum Numbers: Int {
case zero //0
case one //1
case two //2
case ten = 10
}
- 보통 원시값의 타입으로 Int 나 String을 많이 활용하며, 원시값을 따로 지정하지 않는다면
Int: 0, 1, 2, ... 차례대로 자동 매칭
String: case 이름 문자열로 자동 매칭
- 만약 원시값을 가지는 열거형이라면, 원시값을 알 경우 원시값을 통해 인스턴스를 생성할 수 있음
enum RpsGame: Int {
case rock
case paper
case scissors
}
RpsGame(rawValue: 0)!
RpsGame(rawValue: 1)
RpsGame(rawValue: 2) // 자동매칭 되므로 따로 지정하지 않아도 자동 매칭된 원시값을 통해 인스턴스 생성 가능
// 옵셔널값을 안전하게 벗겨서 사용
if let r = RpsGame(rawValue: 0) {
print(r) // rock
}
3) 연관 값을 가지는 열거형
- 보다 구체적인 하위 정보를 저장하고 사용하고싶을 때 유용함
enum Computer {
case cpu(core: Int, ghz: Double)
case ram(Int, String)
case hardDisk(gb: Int)
}
let myChip1 = Computer.cpu(core: 8, ghz: 3.5)
let myChip3 = Computer.ram(16, "DRAM")
let myChip6 = Computer.hardDisk(gb: 128)
* switch문을 활용한 열거형 case 패턴
var chip = Computer.cpu(core: 8, ghz: 2.0)
switch chip {
case .cpu(core: 8, ghz: 3.1):
print("CPU 8코어 3.1GHz입니다.")
case .ram(32, _):
print("32기가램 입니다.")
case .ram(_, _):
print("램 입니다.")
default:
print("그 이외의 칩에는 관심이 없습니다.")
}
// 그 외의 칩에는 관심이 없습니다
// 연관값을 가진 케이스를 패턴 매칭시키기
switch chip {
case let .cpu(a, b): // let a = 연관값, let b = 연관값
print("CPU \(a)코어 \(b)GHz입니다.")
case let .ram(a, _):
print("램 \(a)기가램 입니다.")
case let .hardDisk(a) :
print("하드디스크 \(a)기가 용량입니다.")
}
// cpu 8코어 2.GHz 입니다.
*if/for 문 활용
var chip = Computer.hardDisk(gb: 128)
if case Computer.hardDisk(gb: let gB) = chip { // case let Computer.hardDisk(gb: gB)
print("\(gB)기가 바이트 하드디스크임") // let gB = 열거형 내부의 연관값
}
if case Computer.hardDisk(gb: let gB) = chip, gB == 256 { // if에서의 콤마는 and
print("256기가 바이트 하드디스크임")
}
let chiplists: [Computer] = [
.cpu(core: 4, ghz: 3.0),
.cpu(core: 8, ghz: 3.5),
.ram(16, "SRAM"),
.ram(32, "DRAM"),
.cpu(core: 8, ghz: 3.5),
.hardDisk(gb: 500),
.hardDisk(gb: 256)
]
for case let .cpu(core: c, ghz: h) in chiplists { // 배열중에서 특정 케이스만 뽑아서 활용 가능 ⭐️
print("CPU칩: \(c)코어, \(h)헤르츠")
}
⭐️ 열거형에서 원시값과 연관값을 같이 쓰는 것은 불가능하다.
4) 옵셔널 열거형 (열거형에 연관값이 없고, 옵셔널 열거형인 경우)
- 옵셔널은 .some(자료형)과 .none (nil과 완전히 동일) 으로 이루어진 열거형이다! (해당 포스팅 참고)
enum Direction {
case left
case right
}
let x: Direction? = .left
switch x{
case .left:
case .right:
case nil:
}
// 정식 원칙
switch x{
case .some(let value):
switch value {
case .left:
case .right:
}
case .none:
}
let arrays: [Int?] = [nil, 2, 3, nil, 5]
for case let .some(number) in arrays { // nil을 건너뛰겠다는 뜻
print("Found a \(number)")
}
* 옵셔널 패턴 - switch문
let num: Int? = 7
print(num)
// 1) 열거형 케이스 패턴
switch num {
case .some(let x): // let x = num
print(x)
case .none:
break
}
// 2) 옵셔널 패턴 (.some을 ? 물음표로 대체 가능한 패턴)
switch num {
case let x?: // let x? = Optional.some(num)
print(x)
case .none:
break
}
* 옵셔널 패턴 - if/for 문
// 1) 열거형 케이스 패턴
if case .some(let x) = num {
print(x)
}
// 2) 옵셔널 패턴 (.some을 ? 물음표로 대체 가능한 패턴)
if case let x? = num { // "옵셔널(?) 물음표를 띄어내고, x 상수로 보자"의 의미라고 생각하면 됨
print(x)
}
let arrays: [Int?] = [nil, 2, 3, nil, 5]
// 1) 열거형 케이스 패턴
for case .some(let number) in arrays {
print("Found a \(number)")
}
// 2) 옵셔널 패턴
for case let number? in arrays {
print("Found a \(number)")
}
5) @unknown 키워드
- 최초로 선언 후, 케이스가 늘어날 수도 있는 열거형의 경우에 switch 문에서 모든 열거형을 다루지 않는다면, 노란 경고 라인을 띄워줘서 개발자가 실수를 줄일 수 있도록 도와주는 키워드
enum SNSLogin: String { // 3가지 케이스 ===> 4가지 케이스
case email
case facebook
case google
case kakaotalk
}
let userLogin = SNSLogin.email
switch userLogin {
case .email:
print("이메일 로그인")
case .facebook:
print("페이스북 로그인")
//case .google:
// print("구글 로그인")
default: // default블럭만 추가해두는 것이 안전할까? ⭐️
print("구글 로그인")
}
switch userLogin {
case .email:
print("이메일 로그인")
case .facebook:
print("페이스북 로그인")
case .google:
print("구글 로그인")
@unknown default: // 모든 케이스를 다루지 않았다고 경고를 띄워줘서 개발자의 실수를 줄여줌
print("그 외의 모든 경우")
}
'🐦 Swift' 카테고리의 다른 글
[Swift] 속성(property)와 메소드(method) (0) | 2023.06.08 |
---|---|
[Swift] 구조체(Struct), 클래스(Class) (0) | 2023.06.08 |
[Swift] 연산자 (0) | 2023.05.13 |
[Swift] 변수와 상수, 의미 있는 이름 (0) | 2023.05.09 |
[Swift] 옵셔널, 옵셔널 바인딩, 강제 추출, nil-coalescing (0) | 2023.03.20 |