Swift문법 - (10)열거형(Enum)

Youth·2022년 9월 26일
0

swift문법공부

목록 보기
10/27
post-thumbnail

열거형(Enumerations)

  • 연관된 상수(케이스)들을 하나의 이름으로 묶은 자료형

케이스가 선택가능한(한정됨) 가짓 수로 정해져 있을때 정의

열거형을 사용하면 코드의 가독성과 안정성이 높아짐 ===> 명확한 분기 처리 가능

enum Weekday {
    case monday
    case tuesday
    case sunday
}

enum CompassPoint {
    case north, south, east, west
}

열거형 타입

  • 열거형은 타입이다
// 열거형 타입으로 선언하면 열거형의 요소를 담을 수 있다
var today: Weekday = Weekday.monday
today = .tuesday

// 일요일인 경우만 특정한 처리를 하고 싶을때
if today == .sunday {
}

// 모든 각각의 경우를 처리하고 싶을때
if today == .monday {
    print("오늘은 월요일입니다.")
} else if today == .tuesday {
    print("오늘은 화요일입니다.")
} else {
    print("오늘은 일요일입니다.")
}
  • 열거형은 항상 switch문으로 분기처리 가능
switch today  {
case .monday:  // today == .mondey이면
    print("오늘은 월요일입니다.")
case .tuesday:
    print("오늘은 화요일입니다.")
case .sunday:
    print("오늘은 일요일입니다.")
}

열거형의 원시값

  • 열거형의 원시값(Raw Value)
    • 열거형의 원시값은 매칭되는 기본값(정수/문자열)을 정해, 열거형을 좀 더 쉽게 활용 가능
// 여러가지 형태로 원시값을 정의 가능
// (Hashable한 - Int / String / Character / Double 등 가능)
// 원시값의 타입을 Int로 하면
// 원시값 입력안하면 0, 1, 2  이렇게 자동으로 저장됨
// 정수의 경우 숫자가 하나씩 증가
// 1(설정시), 2, 3
// 0, 2(설정시), 3

enum Alignment: Int {
    case left = 0
    case center
    case right
}

enum Alignment1: String {
    case left = "L"
    case center = "C"
    case right = "R"
}
// 인스턴스 생성시 - 옵셔널타입으로 리턴 (실패가능)
// ✅**rawValue가 없을수도 있기 때문에**✅
let align = Alignment(rawValue: 0)   
// 접근연산자를 통해 원시값 자체에도 접근가능 
let leftValue = Alignment.center.rawValue     

// (가능하지만) 문자열 방식으로는 잘 사용하지는 않음
let center = Alignment1(rawValue: "C")    // center         
let centerValue = Alignment1.center.rawValue

열거형의 연관값(Associated Values)

  • 열거형의 연관값은 구체적인 추가정보를 저장하기 위해 사용
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)

원시값(Raw Values)과 연관값(Associated Values)의 차이

  • 자료형 선언 방식: 선언하는 위치가 다름
  • 선언 형식: (1) 원시값 : 2가지(Int, String)중 1가지 선택 / (2)연관값 : 튜플의 형태로 형식 제한 없음
  • 값의 저장 시점: (원시값: 선언시점 / 연관값: 새로운 열거형 값을 생성할때 )
  • 서로 배타적: 하나의 열거형에서 원시값과 연관값을 함께 사용하는 것은 불가능 함

연관값의 활용

var chip = Computer.cpu(core: 8, ghz: 2.0)

// 일치하는 경우에 return하기
switch chip {
case .cpu(core: 8, ghz: 3.1):
    print("CPU 8코어 3.1GHz입니다.")
case .cpu(core: 8, ghz: 2.6):
    print("CPU 8코어 2.6GHz입니다.")
// _의 의미는 어떤거든 상관이 없다는뜻(와일드카드패턴)
case .ram(32, _):
    print("32기가램 입니다.")
default:
    print("그 이외의 칩에는 관심이 없습니다.")
}

// 특정case를 알맞게 return하기
switch chip {
// let core = 연관값, let ghz = 연관값
case let .cpu(core, ghz):    
    print("CPU \(core)코어 \(ghz)GHz입니다.")
case let .ram(a, _):
    print("램 \(a)기가램 입니다.")
case let .hardDisk(a) :
    print("하드디스크 \(a)기가 용량입니다.")
}

옵셔널 타입에 대한 정확한 이해

연관값이 있는 옵셔널타입
enum Optional<Wrapped> {
     case some(Wrapped)
     case none
 }

var num: Int? = 7
 == Optional.some(7)
 == .some(7)

switch num {
// 열거형 case 패턴을 활용해서, 내부 연관값을 꺼냄
// let a = 7  a에다가 7을 넣을 수 있다면 즉, nil이 아니면
case let .some(a):    
    print(a)
case .none:
    print("nil")
}

열거형과 switch

  • 열거형의 정의와 switch문을 통한 활용
// 로그인 타입을 정의
// rawValue를 String으로 선언후 아무것도 대입하지 않으면 case그대로
enum LoginProvider: String {
    case email
		// case email = "email"
    case facebook
		// case facebook = "facebook"
    case google
		// case google = "google"
}
let userLogin = LoginProvider.facebook

// 열거형은 한정된 사례로 만든 타입이고,
// 스위치문은 표현식에 대한 분기처리에 최적화

switch userLogin {        
case .email:
    print("이메일 로그인")
case .facebook:
    print("페이스북 로그인")
case .google:
    print("구글 로그인")
}

// 물론 특정한 경우도 처리 가능

if LoginProvider.email == userLogin {
    print("이메일 로그인")
}

열거형에 (연관값이 없고), 옵셔널 열거형의 경우

  • switch문의 편의성 (열거형 case 패턴) - Enumeration Case Pattern
enum SomeEnum {
    case left
    case right
}

// 타입을 다시 옵셔널 열거형으로 선언 
let x: SomeEnum? = .left
💡 [SomeEnum?의 의미] 옵셔널 열거형
  • 값이 있는 경우 .some ===> 내부에 다시 열거형 .left /.right
  • 값이 없는 경우 .none ===> nil
switch x {
// ✅Optional.some(let value) = Optional.some(SomeEnum.left)
case .some(let value):     
    switch value {
    case .left:
        print("왼쪽으로 돌기")
    case .right:
        print("오른쪽으로 돌기")
    }
case .none:
    print("계속 전진")
}

// ✅편의적 기능 제공
// switch문에서 옵셔널 열거형 타입을 사용할때, 벗기지 않아도 내부값 접근가능
switch x { 
case .left:
    print("왼쪽으로 돌기")
case .right:
    print("오른쪽으로 돌기")
// case .none도 가능
case nil:
    print("계속 전진")
}

열거형에 연관값이 있는 경우

  • 연관값이 있는 경우, if / guard / for-in / while 문 (열거형 case 패턴)
  • 특정 케이스만 다루기 위해서 if문이나 반복문(for문) 사용 가능
// .hardDisk의 gB를 print하고 싶은데 이Case를 위해 switch문 작성...
switch chip {
case Computer.hardDisk(gb: let gB):      // let gB = 연관값
    print("\(gB)기가 바이트 하드디스크임")
default:
    break
}

// if문으로 특정 case만 처리할 수 있음
if case Computer.hardDisk(gb: let gB) = chip {
    print("\(gB)기가 바이트 하드디스크임")
}

// if 조건문에서 ","의 경우 &&를 의미함 
if case let Computer.hardDisk(gb: gB) = chip, gB == 256 {
    print("256기가 바이트 하드디스크임")
}
  • for문(연관값이 있고 옵셔널 enum타입으로 선언된 경우)
let chiplists: [Computer?] = [
    nil,
    .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)
]

// 배열중에서 특정 케이스만 뽑아서 활용 가능 ⭐️
// 값이존재하는지 안하는지를 보는게아니라 .some을 쓰지 않음
for case .cpu(core: let c, ghz: let h) in chiplists {    
    print("CPU칩: \(c)코어, \(h)헤르츠")
}
  • for문(연관값이 없고 옵셔널 enum타입으로 선언된 경우)
// 옵셔널 타입을 포함하는 배열에서 반복문을 사용하는 경우

let arrays: [Int?] = [nil, 2, 3, nil, 5]

// .some은 내부에 값이 있는 경우임 즉, 내부에 값이 있는것만 print하겟다는 의미
// optional에서 enum은 .some과 .none 두가지임
for case .some(let number) in arrays {
    print("Found a \(number)")
}

옵셔널 패턴(Optional Pattern)

let a: Int? = 1

// 1) 열거형 케이스 패턴
switch a {
case .some(let z):
    print(z)
case .none:  // nil이라고 써도됨
    print("nil")
}

// 2) 옵셔널 패턴
switch a {
case let z?:      // .some을 조금 더 간소화하는 문법
    print(z)
case nil:         // .none 이라고 써도됨
    print("nil")
}
  • if문으로 특정case만 다루고 싶은 경우
    • 값이 존재하는지 안하는지를(옵셔널인경우) .some타입으로 바인딩을 해야함
    • .some 대신 ?를 사용한다고 생각하면됨
// 특정 사례만 다루는 (if문)
// 1) 열거형 케이스 패턴
if case .some(let x) = num {
    print(x)
}

// 2) 옵셔널 패턴 (.some을 ? 물음표로 대체 가능한 패턴)
if case let x? = num {
    print(x)
}
  • for문을 돌면서 각각의 타입의 옵셔널여부를 체크하고 싶은경우
let arrays: [Int?] = [nil, 2, 3, nil, 5]

//특정케이스만 for 문을 돌리는 방법
// 1) 열거형 케이스 패턴
// 연관값이존재하는경우다(optional의 경우)
for case .some(let number) in arrays {
    print("Found a \(number)")
}

// 2) 옵셔널 패턴
for case let number? in arrays {
    print("Found a \(number)")
}

세줄요약

연관값이 없고 열거형이 옵셔널로 선언되어있는경우
→ 두번 벗길필요가 없다(.some을 바인딩해서 그안에서 .left .right할필요X)

연관값이 있는경우 모든케이스를 다루고 싶지 않은 경우 조건문반복문에 case를 바인딩

표현식이 옵셔널 타입인 경우 - 옵셔널 패턴(.some대신 ?)
→ let a: Int? = 1 ⇒ 이경우가 표현식이 옵셔널인 경우이다

profile
AppleDeveloperAcademy@POSTECH 1기 수료, SOPT 32기 iOS파트 수료

0개의 댓글