[Swift] 스위프트 기초 - 데이터 타입 고급

koi·2022년 9월 7일
1
post-thumbnail

📌 Swift 데이터 타입 고급

데이터 타입 안심

  • 스위프트는 안정성을 강조하는 언어인 만큼, 타입에 굉장히 민감하고 엄격하다.
  • 서로 다른 타입끼리의 데이터 교환은 꼭 타입캐스팅을 거쳐야한다.
    ❗️ 스위프트에서 값 타입의 데이터 교환은 엄밀히 말하면 타입 캐스팅이 아닌 새로운 인스턴스를 할당하는 것.

스위프트는 데이터 타입을 안심하고 사용할 수 있는, Type-safte 언어이다. 스위프트가 컴파일 시 타입을 확인하는 것을 타입 확인이라고 한다.

타입 추론

  • 스위프트에서는 변수나 상수를 선언할 때 특정 타입을 명시하지 않아도 컴파일러가 할당된 값을 기준으로 변수나 상수의 타입을 결정한다.
    ❗️빈 배열을 만들 경우에는 타입을 반드시 명시해야 한다.

타입 별칭 (typealias)

스위프트에서 기본으로 제공하는 데이터 타입이든, 사용자가 임의로 만든 데이터 타입이든, 이미 존재하는 데이터 타입에 임의로 다른 이름(별칭)을 부여할 수 있다.

typealias MyInt = Int
typealias YourInt = Int
typealias MyDouble = Double

let age: MyInt = 100
var year: YoutInt = 2080

year = age
let month: Int = 7
let percentage: MyDouble = 99.9

튜플

  • 튜플은 타입의 이름이 따로 지정되어 있지 않은, 프로그래머 마음대로 만드는 타입이다. 지정된 데이터의 묶음이라고 표현할 수 있다.
  • C언어를 예로 들자면 원시 구조체의 형태와 가깝다.
  • 튜플은 타입 이름이 따로 없으므로 일정 타입의 나열만으로 튜플 타입을 생성해줄 수 있다.

튜플의 사용

var person: (String, Int, Double) = ("toma", 100, 182.5)
// 인덱스를 통해서 값을 빼오고, 할당할 수도 있다.
person.1 = 99
person.2 = 178.5

이렇게 인덱스를 통해 요소에 접근 할 수 있지만, 차후에 각 요소의 어떤 의미가 있는지 유추하기가 어렵다는 점이 있다.

그래서 튜플의 요소마다 이름을 붙여줄 수도 있다.

튜플에 이름 붙이기

var person: (name: String, age: Int, height: Double) = ("toma", 100, 182.5)

person.age = 99
person.2 = 178

이런식으로 하면 이름으로도, 인덱스로도 접근이 가능하다.

🤔 그럼 tuple은 컬렉션 타입이 아닐까?

참고
Swift에서 튜플은 Collection type이 아니다!
Swift에서 타입은 크게 named typecompound type이 존재하는데, named type은 우리가 주로 알고있는 기본타입들 외에도 enum, class, struct와 collection type들이 이에 해닿되고 comound type에 tuple이 해당된다!

컬렉션형

  • 많은 수의 데이터를 묶어서 저장하고 관리할 수 있는 데이터 타입
  • 배열, 딕셔너리, 세트 등이 있다.

배열 (Array)

  • 배열은 같은 타입의 데이터를 일렬로 나열한 후 순서대로 저장하는 형태의 컬렉션 타입
  • 각기 다른 위치에 같은 값이 들어갈 수도 있다.

배열의 사용

  • 배열은 각 요소에 인덱스를 통해 접근할 수 있다. (0부터)
  • 잘못된 인덱스로 접근하려고 하면 Exception error가 발생한다.
  • 맨 처음과 맨 마지막 요소는 first, last 프로퍼티로 접근
  • index(of:) 메서드로 해당 요소의 인덱스를 알아낼 수 있다.
    중복된 요소가 있다면 제일 먼저 발견된 인덱스를 반환.
  • 맨뒤에 요소를 추가할 때는 append(_:) 메서드
  • 맨뒤에 시퀀스 요소를 추가할 때는 append(contentsOf:) 메서드
  • 중간의 요소를 삽입할 때는 insert(_:,at:) 메서드
  • 요소를 삭제하고 싶다면 remove(_:)메서드를 사용
    해당 요소가 삭제된 후, 요소가 반환된다.

정식 표현

var names: Array<String> = ["yagom", "toma", "swift", "toma"]

축약 표현

var names: [String] = ["yagom", "toma", "swift", "toma"]

var emptyArray: [Any] = [Any]()
var emptyArray: [Any] = Array<Any>() // 위와 동일한 의미의 코드

딕셔너리 (Dictionary)

  • 요소들이 순서없이 키와 값의 쌍으로 구성되는 컬렉션 타입이다.
  • 딕셔너리에 저장되는 값은 항상 키와 쌍을 이룬다.
  • 하나의 딕셔너리에서는 같은 키의 이름이 중복될 수 없다.
    즉, 딕셔너리에서 키는 값을 대변하는 유일한 식별자이다.
typealias StringIntDictionary = [String: Int]
// 모두 같은 의미의 코드
var numberForName: Dictionary<String: Int> = Dictionary<String, Int>() // 빈 딕셔너리 생성
var numberForName: [String: Int] = [String: Int]()
var numberForName: StringIntDictionary =StringIntDictionary()
var numberForName: [String: Int] = [:]

딕셔너리의 사용

  • 딕셔너리는 각 값에 키로 접근할 수 있다.
  • 딕셔너리 내부에서 키는 유일해야 하지만, 값은 유일하지 않다.
  • 딕셔너리는 배열과 다르게 내부에 없는 키로 접근해도 오류가 발생하지 않는다
    nil을 반환
  • 특정 키에 해당하는 값에 접근하려면 dictionary[key]
  • 특정 키에 해당하는 값을 제거하려면 removeValue(forKey:)
    키에 해당하는 값이 제거된 후 반환

세트

  • 같은 타입의 데이터를 순서없이 하나의 묶음으로 저장하는 형태의 컬렉션 타입이다.
  • 세트 내의 값은 모두 유일한 값, 즉 중복된 값이 존재하지 않는다.
  • 보통 순서가 중요하지 않거나, 각 요소가 유일한 값이어야 하는 경우 사용한다.
  • 세트의 요소로는 *해시가능한 값이 들어와야한다.
    해시가능한 값이란 스위프트 표준 라이브러리의 Hashable 프로토콜을 따른다는 것을 의미. 스위프트의 기본 데이터 타입은 모두 해시가능한 값.

세트의 사용

  • 세트는 배열과 마찬가지로 대괄호로 값을 묶어 세트 타입임을 표현한다.
  • 배열과 달리 줄여서 표현할 수 있는 축약형이 없다.
  • 요소를 추가하고 싶다면 insert(:_)
  • 요소를 삭제하고 싶다면 remove(:_)
    해당 요소가 삭제된 후 반환
  • 집합관계를 사용하고자 할 때 유용하게 사용
    교집합 intersection(:_)
    여집합의 합 .symmetricDifference(:_)
    합집합 .union(:_)
    차집합 .subtracting(:_)
  • sorted() 메서드를 통해 정렬된 배열을 반환할 수 있다.

세트의 활용

세트는 포함관계를 연산할 수 있는 메서드로 구현되어있다.

  • 서로 배타적인지 isDisjoint(with:)
  • 부분집합 isSubset(of:)
  • 전체집합 isSuperSet(of)

열거형

  • 연관된 항목들을 묶어서 표현할 수 있는 타입이다.
  • 배열이나 딕셔너리 같은 타입과 다르게 프로그래머가 정의해준 값 외에는 추가/수정이 불가하다.
  • 딱 정해진 값만 열거형 값에 속할 수 있다.
  • 기존 C 언어 등에서 열거형은 주로 정수 타입 값의 별칭 형태로 사용될 뿐이었지만, 스위프트의 열거형은 각 열거형이 고유의 타입으로 인정된다.

열거형은 다음과 같은 경우 요긴하게 사용될 수 있다.

  • 제한된 선택지를 주고 싶을 때
  • 정해진 값 외에는 입력받고 싶지 않을 때
  • 예상된 입력 값이 한정되어 있을 때

기본 열거형

  • enum 키워드로 선언
enum School {
	case primary
    case elementary
    case middle
    case high
    case collage
    case university
    case graduate
}
// 같은 의미
enum School {
	case primary, elementary, middle, high, collage, university, graduate
}
var highhestEducationLevel: School = School.university
var highhestEducationLevel: School = .university // 같은 의미

원시 값

  • 열거형의 각 항목은 자체로도 하나의 값이지만, 항목의 원시 값도 가질 수 있다.
  • 특정 타입의 값을 원시 값으로 가지고 싶다면 열거형 이름 오른쪽에 타입 명시
  • rawValue 프로퍼티를 통해 원시 값 사용 가능
enum Numbers: Int {
	case zero // rawValue = 0
    case one // rawValue = 1
    case two // rawValue = 2
    case ten = 10 // rawValue = 10
}

일부 항목만 원시값을 주는 것도 가능하다.
나머지는 스위프트가 자동처리해준다.

연관 값

  • 열거 내 항목(case)이 자신과 연관된 값을 가질 수 있다.
  • 연관 값은 각 항목 옆에 소괄호로 묶어서 표현
enum MainDish {
	case pasta(taste: String)
    case pizza(dough: Stirng, topping: String)
    case chicken(withSauce: Bool)
    case rice
}

var dinner: MainDish = MainDish.pasta(taste: "크림")
dinner = .pizza(dough: "치즈 크러스트", topping: "불고기")

항목 순회

단순한 열거형

CaseIterable 프로토콜을 채택하면 열거형의 allCases 프로퍼티로 모든 케이스의 컬렉션을 생성할 수 있다.

복잡한 열거형

  • ex) available 속성을 통해 특정 케이스를 플랫폼에 따라 사용할 수 있거나 없는 경우가 생기면 CaseIterable 프로토콜을 채택하는 것만으로 allCases 프로퍼티를 사용할 수 없다.
    이런 경우 직접 allCases 프로퍼티를 구현해 주어야 한다.
  • 케이스가 연관 값을 갖는 경우도 이에 해당한다.

순환 열거형

  • 순환 열거형은 열거형의 항목의 연관 값이 열거형 자신의 값이고자 할 때 사용한다.
  • indirect 키워드를 사용한다.
  • 이진 탐색 트리 등의 순환 알고리즘을 구현할 때 유용하게 사용 가능
enum ArithmeticExpression {
	case number(Int)
    indirect case addition(ArithmeticExpression, ArithmeticExpression)
    indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
}
// enum 자체에 키워드 붙이기도 가능
indirect enum ArithmeticExpression {
	case number(Int)
    case addition(ArithmeticExpression, ArithmeticExpression)
    case multiplication(ArithmeticExpression, ArithmeticExpression)
}

비교 가능한 열거형

  • Comparable 프로토콜을 준수하는 연관 값만 갖거나,
    연관 값이 없는 열거형Comparable 프로토콜을 채택하면 각 케이스를 비교할 수 있다.
  • 앞에 위치한 케이스가 더 작은 값
profile
Don't think, just do 🎸

0개의 댓글