Swift에서는 Foundation Collection과 Swift Collection이 존재한다. 스위프트 컬렉션은 구조체로 이루어 져있으며 값타입이다. 그리고 하나의 타입들로만 구성할 수 있고, 객체와 값을 전부 저장이 가능하다.
Foundation Collection은 클래스로 이루어져있으며, 참조형식이다. 내부 저장할 요소가 다른 타입이여도 상관이없다. 하지만 객체형식으로만 저장이 가능하다. 이름으로는 접두어에 NS
가 붙는 차이가 있다. 가변 컬렉션으로 만드려면 접두어에 NSMutable
을 붙여주면 된다.
let fruits = ["apple", "banana", "cherry"]
// first, last는 값이 없다면 nil을 반환하는 옵셔널 타입이다. 좀 더 안전하다
fruits.first // apple
fruits.last // cherry
fruits[fruits.index(before:fruits.endIndex)] // cherry
var alphabet = ["A", "B", "C"]
// 마지막 요소에 추가한다.
alphabet.append("E")
alphabet.append(contentsOf: ["F", "G"])
// 특정 위치에 추가한다.
alphabet.insert("D", at: 3)
alphabet.insert(contentsOf: ["a", "b" ,"c"], at: 0)
// 특정 범위의 배열 요소를 변경하는 코드
alphabet[0...2] = ["x", "y", "z"]
alphabet[..<1] = [] // 멘 앞의 요소가 삭제가된다.
// 특정 인덱스에 요소를 제거하고 리턴함
alphabet.remove(at: 2)
// 처음 요소를 제거하고 리턴함
alphabet.removeFirst()
// 앞 두개를 제거하고 리턴은 하지 않는다
alphabet.removeFirst(2)
// 전부 삭제한다
alphabet.removeAll
// 좀 더 안전한 방식의 제거, 옵셔널 타입을 반환
alphabet.popLast()
// 특정 범위의 요소를 제거
alphabet[0...2] = []
let a = ["A", "B", "C"]
let b = ["a", "b", "c"]
// 같은지 판단
a.elementsEqual(b)
// 대소문자 구별 x
a.elementsEqual(b) { (lhs, rhs) -> Bool in
return lhs.caseInsesitiveCompare(rhs) == .orderedSame
}
// 축약하면
a.elementsEqual(b) {
$0.caseInsensitiveCompare($1) == .orderedSame
}
let nums = [1,2,3,4,5,6,7,0]
nums.contains(1) // true
nums.contains(8) // false
// 짝수임을 확인하는 코드, 클로저로 전달하였다.
nums.contains { (n) -> Bool in
return n % 2 == 0
}
// 처음 나온 짝수 요소의 값을 리턴한다. 여기서는 2
nums.first { (n) -> Bool in
return n % 2 == 0
}
// 처음 나온 짝수의 인덱스를 리턴함. 여기서는 1
// 이 메소드는 값을 전달도 가능하다. e.g. (of: 1) -> 0
nums.firstIndex { (n) -> Bool in
return n % 2 == 0
}
sort -> 배열을 직접 정렬 , sorted -> 정렬된 새로운 배열을 리턴
let sortedNums = nums.sorted()
// 내림차순 정렬 구현
nums.sorted { (a, b) -> Bool in
return a > b
}
// 배열 생성자를 사용해서 생성해줌, 역순
[Int]nums.sorted().reveresed()
// 특정 인덱스 끼리의 순서를 바꾸는 메소드
mutableNums.swapAt(0, 1)
words = ["A" : "Apple", "B" : "Banana", "C" : "City"]
// 먼저 파라미터로 튜플을 받고 불린형을 리턴하는 클로저를 구현해야한다
// 키의 요소에 "B" 가 있거나 값의 i 가 포함되면 true
let c: ((String, String)) -> Bool = {
$0.0 == "B" || $0.1.contains("i")
}
words.contains(where: c) // true
집합의 연산에 대해서 알아보겠습니다.
var a : Set = [1,2,3,4,5,6]
var b : Set = [1,3,5]
var c : Set = [10,20,30]
a.isSubset(of: a) // true 자기 자신에 대해 부분집합이므로
a.isStrictSubset(of: a)// false 자기 자신은 진부분집합 x
b.isSubset(of: a) // true
b.isStrictSubset(of: a) // true\
// 교집합이면 false, 서로소 이면 true를 리턴한다.
a.isDisjoint(with: b) // false
// 합집합
var result = b.union(c)
// 원본을 변경하는 합집합
b.formUnion(c)
// 교집합
result = a.intersection(b)
b.intersection(a)
// 여집합
result = a.symmetricDifference(b)
a.symmetricDifference(b)
// 차집합
result = a.subtracting(b)
a.subtract(b) // 원본 변경
컬렉션을 열거하는 방법에대해 알아보겠습니다.
for-in을 활용하는 방법이 있습니다.
let arr = [1, 2, 3]
for num in arr {
print(num)
}
// 딕셔너리는 루프상수로 튜플을 받아야한다.
let dict = ["a" : 1, "b" : 2, "c" , 3]
for (key, value) in dict{
print(key, value)
}
다음으로 forEach문입니다. 파라미터를 클로저로 받습니다.
// 위의 for-in과 같다
arr.forEach { (num) in
print(num)
}
// 튜플로 받는다.
dict.forEach { (tup) in
print(tup.key, tup.value)
}
두 방식의 차이로는 함수로 사용할 시에 return문에서 차이가 있다. forEach 내부의 return은 외부에 영향을 주지않고 현재 실행중인 클로저만 종료하기 때문에 반복회수에는 영향이 없습니다.
하지만 for-in문 내부에 return은 외부에도 영향이 가기 때문에, 함수가 종료되고, 반복횟수에도 영향이 있습니다.
딕셔너리와 배열의 특징을 가진 KeyValuePairs에 대해 알아보겠습니다.
특징으로는 정렬이 되어 있고, Hashable을 채용하지 않아서 Key 가 중복이 될 수 있지만, 검색속도가 느리다.
let words: KeyValuePairs<String, String> = ["A" : "Apple", "B" : "Banana", "C" : "City"]
키와 값을 쌍으로 연결해야하고, 정렬 되어 있어야하고, 동일한 값을 저장하는 경우가 있다면 사용하면 좋을 듯하다.
열거형에 대해서 알아보겠습니다, 코드의 가독성과 안전성을 위해 주로 활용합니다. 이해하기 쉽게 열거형 케이스는 엄연히 값이라고 생각하면 쉽다.
// 문법은 다음과 같다
enum TypeName {
case caseName
case caseName1, caseName2
}
// 정렬 기능을 구현하는 예시
enum Alignment{
case left
case right
case center
}
Alignment.center
var textAlignment = Alignment.center
// 이미 타입추론 가능하므로 열거형 을 생략함
textAlignmnet = .left
내부에 값을 저장하는 원시값에 대해 알아보겠습니다. 선언을 하면 나중에 바꿀수는 없습니다.
// 문법은 다음과 같습니다.
enum TypeName: RawValueType(String, Character, Number Types) {
case caseName = Value
}
// 0,1,2로 값이 적용된다. 값을 하나 저장하면 다음 값은 1을 더한 값이다.
enum Alignment: Int{
case left
case right
case center
}
Alignment.left.rawValue // 0
Alignment(rawValue: 0) // left를 리턴한다. 해당 값이 없다면 nil을 리턴
// 문자열로 타입을 선언하고 원시값을 설정하지 않으면 caseName이 값이된다.
// Character 타입은 원시값을 직접 저장해야만 한다.
enum Weekday: String {
case sunday
case monday = "Mon"
...
}
Weekday.sunday.rawValue // "sunday"
Weekday.monday.rawValue // "Mon"
연관값에 대해서 알아보고, 원시값이랑 차이를 알아보겠습니다.
연관값은 튜플로 선언한다.
// 해당 문법
enum TypeName {
case caseName(Type)
case caseName1(Type, Type, ...)
}
enum VideoInterface {
case dvi(width: Int, height: Int)
case hdmi(Int, Int, Double, Bool)
case displayPort(CGSize)
}
var input = VideoInterface.dvi(width: 2048, height: 1536)
switch input {
case .dvi(2048, 1536) :
print("dvi 2048 * 1536")
case .dvi(2048, _) :
print("dvi 2048 * Any")
case .dvi :
print("dvi")
case .hdmi(let width, let height, let version, var audioEnabled) :
print("hdmi \(width) * \(height)")
// 모든 연관값을 동일한 형태로 바인딩 한다면 let 키워드를 앞에 표기도 가능하다.
// 이 패턴을 Enumeration Case Pattern 이라고 한다.
case let .displayPort(size) :
print("dp")
}
연관값을 가지는 열거형 케이스를 매칭하는 패턴에 대해서 알아보겠습니다.
// 다음 문법
case Enum.case(let name):
case var Enum.case(name):
enum Transprotation {
case bus(number: Int)
case subway(lineNumber: Int, express: Bool)
}
switch tpt {
case .bus(let n) :
print(n)
case let subway(l, e):
print(l, e)
}
// tpt가 2이면 바인딩이 되므로, if문 내부를 실행 한다.
// 앞의 let 키워드가 있으므로 express는 바인딩되어서 내부에서 상수로 사용가능하다.
// 바인딩을 하지 않을거라면, let키워드는 생략 가능
if case let .subway(2, express) = tpt {
...
}
// for where 사용 예시
// n은 루프상수로, where절을 활용해서 조건을 추가하였다
for case let .subway(n, true) in list where n == 2 {
...
}
모든 케이스를 열거할수 있게 도와주는 프로토콜에 대해 알아보겠습니다.
// CaseIterable 프로토콜을 선언하면, 배열형태로 열거가 가능하다.
enum Weekday: Int, CaseIterable {
case sunday
case monday
case tuesday
case wednesday
...
}
// 하나의 요일을 랜덤으로 추출
let rnd = Int.random(in: 0...Weekday.allCases.count)
// 좀 더 정확한 방법
Weekday.allCases.randomElement()
// .allCases가 배열을 리턴하므로 반복문에서 사용가능
for day in Weekday.allCases {
print(day)
}
선언한 열거형에 추가할 수 있다고 하여 Nonfrozen 열거형이라고 부릅니다. 그리고 해당 케이스를 전부 처리하지 않는 경우를 판단할 수 있게하는 @unknown default
에 대해 알아보겠습니다.
enum ServiceType {
case onlineCourse
case offlineCamp
}
let selectedType = ServiceType.onlineCourse
switch selectedType {
case .onlineCourse:
print("~~")
case .offlineCamp:
print("hihi")
// 케이스 전부를 처리하고 있지 않음을 알려주는 기능, 경고로 알려준다.
@unknown default:
break
}