[내일배움캠프] 260114 TIL - 열거형

Bambu·2026년 1월 14일

내배캠 TIL

목록 보기
19/52

1. 모닝 스터디 - 알고리즘

1) 프로그래머스 - 치킨 쿠폰
2) 프로그래머스 - 리스트 자르기
3) 프로그래머스 - 배열 조각하기

2. 프로그래밍 심화 주차 - 야구 게임 만들기

1) 열거형 활용하기

코드 내 등장하는 여러 게임 메세지들을 하나의 열거형으로 분리하고 있다.
대부분은 단순히 문자열을 원시값으로 정의하면 됐었는데, 힌트를 주는 부분에서 난관에 부딪혔다.

        // 힌트에 따른 분기 처리
        if hint.strike == 3 {
            isCorrect = true
            print("🎉 정답입니다!\n")
        } else if hint.strike == 0 && hint.ball == 0 {
            print("❌ Nothing\n")
        } else {
            print("🎯 \(hint.strike) 스트라이크 ⚾️ \(hint.ball) 볼 입니다!\n")
        }

기존의 힌트는 (strike: Int, ball: Int) 타입의 hint 변수에 접근하여 문자열을 출력하고 있었다.

하지만 게임 메세지는 문자열을 원시값으로 채택한 열거형으로 구현중이었다.

enum SystemMessage: String {
    case welcome = """
            환영합니다! 원하시는 번호를 입력해주세요. (예: 1)
            1. 게임 시작하기  2. 게임 기록 보기  3. 종료하기
            """
    
    case gameStart = "\n❮ 게임을 시작합니다. ❯"
    
    ...
    
}

하지만 이 경우, strike와 ball에 따른 메세지를 출력하지 못한다.

케이스 별로 다른 연관값을 주어 해결하려 했지만, 그 경우 외부에서 열거형 객체를 생성해주어야한다는 단점이 있었다.

enum SystemMessage {
	case welcome (String)
    case hint (strike: Int, ball: Int)
}

// 위 처럼 정의할 경우 따로 객체를 생성해야함
let message = SystemMessage.welcome("환영합니다!")

이 방법은 번거롭다고 생각했다.

그래서 별도의 Hint 열거형을 생성하여 해결해보려 했다.

enum Hint {
	case hint(strike: Int, ball: Int)
    
    func toString() -> String {
    	switch self {
        case .hint(let s, let b):
        	return "\(s) 스트라이크 \(b) 볼 입니다!\n"
        }
    }
}

이렇게 하면 가능은 한데...

        // 힌트에 따른 분기 처리
        if hint.strike == 3 {
            isCorrect = true
            print("🎉 정답입니다!\n")
        } else if hint.strike == 0 && hint.ball == 0 {
            print("❌ Nothing\n")
        } else {
            let m = Hint.hint(strike: hint.strike, ball: hint.ball)
            print(m.toSring())
        }

이 경우에는 그럼 Hint.hint.toString 내에서 분기 처리해서 케이스별 메세지를 내보내는 게 낫지 않나? 라는 고민이 생겼다.

그런데! 그렇게 하면 '정답을 비교한다'라는 기능을 checkAnswer 함수가 아니라 메세지 표시 열거형에서 하지 않나?라는 생각을...

→ 지금 생각해보니 메세지만 처리하는 거니 괜찮지 않을까 싶다.

그리고 메세지를 생성하기 위한 코드가 한 줄이 더 들어간다는 게... 조금 마음에 걸려 방법이 없을지 튜터님께 여쭤보았다.

그리고 알려주신 세 가지 방법!

가. 확장과 프로토콜 활용하기

SystemMessage 열거형을 확장하여 CustomStringConvertible 프로토콜을 채택하는 방법

enum SystemMessage {
	case welcome
    case hint(strike: Int, ball: Int)
}

extension SystemMessage: CustomStringConvertible {
	var description: String {
    switch self {
    case .welcome: 
    	return "환영합니다!"
    case .hint(let s, let b):
    	return s == 3 ? "🎉 정답입니다! 🎉"
            : s == 0 && b == 0 ? "❌ Nothing\n" :
            "🎯 \(s) 스트라이크 ⚾️ \(b) 볼 입니다!\n"
}

→ CustomStringConvertible 프로토콜은 description 변수를 정의하여 케이스 별로 문자열 값을 사용가능하도록 한다.

여기서 정의해둔 문자열 description 값은 String Interpolation으로 열거형을 사용할 때 description에 접근할 필요 없이 자동으로 사용된다고 한다!

print("\(SystemMessage.welcome)") // "환영합니다!" 출력

나. 타입 변수 활용하기

enum 혹은 struct 타입에 타입 변수와 타입 메서드를 활용하는 방법

enum SystemMessage {
	static var welcome = "환영합니다!"
    static var hint = ""
    
    static func getHint(for strike: Int, _ ball: Int) {
            hint = strike == 3 ? "🎉 정답입니다! 🎉"
        : strike == 0 && ball == 0 ? "❌ Nothing\n" :
        "🎯 \(strike) 스트라이크 ⚾️ \(ball) 볼 입니다!\n"
	}
}

→ 열거형을 선언하고 확장해줄 필요 없이 한방에 선언 가능!

💡 보통 열거형으로 사용하는 이유?
위 예시와 같은 용도로 사용할 경우, 따로 systemMessage라는 객체를 만들 필요가 없음. 그러니 인스턴스가 만들어질 수 있는 구조체보다 열거형을 사용!
-> 혹시나 인스턴스가 만들어진다거나 하는 걸 방지하는 목적이 아닐지?

3. 수준별 분반

1) ARC

unowned와 weak의 차이

weak를 사용하면 HeapObject 내부에 side...Table이 생겨서 메모리를 더 사용
→ side 무슨 테이블은 64byte, unowned를 처리하는 테이블은 8byte라서 용량에 차이가 있음!

하지만! 둘 중 뭘 써야할지 모르겠다면 weak를 쓸 것!
→ unowned는 참조할 녀석이 없을 때 nil값을 반환해주지 않고 런타임 에러를 일으킴
⇒ 메모리 조금 더 잡아먹는게 앱의 안전성을 낮추는 것보다 낫다!!

2) Memory Leak

XCode는 모든 메모리 누수를 잡아주지는 못함
→ Swift의 ARC는 자동으로 메모리를 관리하는 무언가가 아니라, RC를 증가시키고 감소시키는 함수를 코드별로 넣어주는 것뿐임.
⇒ 그래서 순환 참조 같은 경우는 컴파일러가 알아내기 힘듦

→debugSwift를 통해 메모리 누수가 일어나는 지점을 알아내고, XCode의 Memory Graph Debugger를 통해 원인을 알아내고 수정
(실습 진행)

profile
안녕하세요, iOS 개발을 공부하고 있는 Bambu입니다. (프로필: Swifticons)

0개의 댓글