📱 iOS/Swift

[Swift] indirect enum 문법

kyxxn 2024. 7. 17. 17:31

문제상황

'<P><IMG SRC="camp.jpg">camp</IMG></P>' 와 같은 XML 태그가 주어지면,
이를 Parser 할 때, P 태그 자식인 IMG 태그는 Token을 어떻게 분류하고 관리할까 ?

struct로 관리하면 자신을 참조할 수 없어 문제가 생긴다. 물론, class를 써도 된다.
그러나 class를 쓰지 않고, 재귀적인 방법을 찾아보다가 indirect enum 문법을 발견했다.

indirect enum의 개념

  • Swift의 열거형(enum)에서 재귀적인 데이터 구조를 표현할 수 있도록 해주는 문법
  • 열거형의 케이스가 자기 자신을 포함할 수 있게 만들어서
    재귀적으로 중첩된 데이터 구조를 명확하고 간결하게 정의

언제, 왜 사용할까 ?

일반적으로 열거형은 값 타임임
값 타임은 그 크기를 컴파일 때 알 수 있어야 하는데,
재귀적 구조를 사용하면 자기 자신을 포함하는 구조체 || 열거형은 크기 결정이 안된다.

indriect 키워드를 통해 이런 제한을 우회하여 간접적으로 자기 자신을 참조

indirect enum 사용하기

1번 방법: 열거형 전체를 indirect로

: 열거형의 모든 케이스가 재귀적인 구조를 가짐

indirect enum Element {
    case element(tagName: String, attributes: [String: String]?, subTags: [Element]?)
    case content(String?)
}

2번 방법: 특정 케이스만 indirect로 정의

: 특정 케이스에만 재귀적인 구조를 허용함

enum Element {
    indirect case element(tagName: String, attributes: [String: String]?, subTags: [Element]?)
    case content(String?)
}

실습

// <P><IMG SRC="camp.jpg">camp</IMG></P>
indirect enum Element {
    case element(tagName: String, attributes: [String: String]?, subTags: [Element]?)
    case content(String?)
}

let imgElement = Element.element(tagName: "IMG", attributes: ["SRC": "camp.jpg"], subTags: nil)
let pElement = Element.element(tagName: "P", attributes: nil, subTags: [
    imgElement,
    .content("camp")
])

func printElement(_ element: Element, indent: String = "") {
    switch element {
    case let .element(tagName, attributes, subTags):
        let attrString = attributes?.map { "\($0.key)=\"\($0.value)\"" }.joined(separator: " ") ?? ""
        print("\(indent)<\(tagName) \(attrString)>")
        if let subTags = subTags {
            for child in subTags {
                printElement(child, indent: indent + "  ")
            }
        }
        print("\(indent)</\(tagName)>")
    case let .content(content):
        print("\(indent)\(content ?? "")")
    }
}

printElement(pElement)

camp

``` ```