Swift/개념 & 응용

[Swift] CustomStringConvertible - 구조체, 클래스 출력하기

유정주 2022. 12. 22. 18:55
반응형

CustomStringConvertible 소개

Swift에서 구조체나 클래스를 출력하면 저희가 원하는대로 나오지 않는 경험 다들 한 번씩은 해보셨을 겁니다.

예를 들어,

class Point {
    var x: Int
    var y: Int
    
    init(x: Int, y: Int) {
        self.x = x
        self.y = y
    }
}

이런 Point 클래스가 있을 때 이 객체를 출력할 때 x와 y 값을 확인하고 싶다고 합시다.

 

let point = Point(x: 1, y: 2)
print(point) //__lldb_expr_15.Point

그래서 point 객체를 출력하면 x, y가 아니라 객체 정보가 출력이 돼요.

 

클래스를 구조체로 바꾸면

클래스보다는 낫지만 우리가 흔히 사용하는 (x, y) 형태가 아니라 구조체 이름과 프로퍼티 이름이 함께 출력됩니다.

 

이런 문제점을 해결하기 위해 많은 분들은 아래처럼 계산 프로퍼티를 사용하셨을 것입니다.

class Point {
    var x: Int
    var y: Int
    
    var description: String {
        return "(\(x), \(y))"
    }
    
    init(x: Int, y: Int) {
        self.x = x
        self.y = y
    }
}

let point = Point(x: 1, y: 2)
print(point.description) //(1, 2)

물론 이 방법도 원하는대로 동작은 하지만 객체의 프로퍼티에 직접 접근해서 출력하기 때문에

코드가 가로로 길어진다는 단점이 있습니다.

 

오늘 소개드릴 CustomStringConvertible를 사용하면

print(point)

만으로 원하는 결과를 얻을 수 있습니다.

 

참고로 배열이나 딕셔너리 등을 print로 출력하면

[(0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0)]

이런 식으로 보기좋게 나오잖아요?

 

이렇게 예쁘게 출력되는 이유도

Array, Dictionary 등등에서 CustomStringConvertible를 준수하고 있기 때문입니다.

Array는 이런식으로 description을 구현했네요 ㅎㅎ

 

CustomStringConvertible 사용법

먼저 공식 문서를 먼저 보겠습니다.

CustomStringConvertible은 프로토콜 중 하나로,

타입을 표현하는 방법을 커스텀할 수 있습니다.

 

프로토콜의 설명을 보면

If the passed instance conforms to CustomStringConvertible, the String(describing:) initializer and the print(_:) function use the instance’s custom descriptionproperty.

라고 저희가 원하는 것을 명확히 말하고 있습니다.

 

CustomStringConvertible은 description 프로퍼티를 필수로 구현해야 합니다.

description 프로퍼티는 getter만 가능한 계산 프로퍼티이고, 

우리가 출력하고 싶은 형태를 반환해주면 됩니다.

 

CustomStringConvertible 예시

이제 CustomStringConvertible를 직접 사용해 봅시다.

Point 구조체에서 CustomStringConvertible를 채택하면 description 프로퍼티를 구현하라는 컴파일 에러가 발생합니다.

컴파일러가 말하는대로 구현을 해봅시다.

struct Point: CustomStringConvertible {
    var x: Int
    var y: Int
    
    var description: String {
        return "(\(x), \(y))"
    }
}

description 프로퍼티를 구현했습니다.

description 프로퍼티에 원하는 출력 형태를 반환해주면 됩니다.

간단하죠?

 

결과를 보면,

let point = Point(x: 1, y: 2)
print(point) //(1, 2)

point만 출력해도 우리가 원하는 형태로 나오는 것을 볼 수 있습니다.

물론!

print(point.description) //(1, 2)

description 프로퍼티에 접근해서 출력하는 것도 가능합니다.

 

class Point: CustomStringConvertible {
    var x: Int
    var y: Int
    
    var description: String {
        return "(\(x), \(y))"
    }
    
    init(x: Int, y: Int) {
        self.x = x
        self.y = y
    }
}

let point = Point(x: 1, y: 2)
print(point) //(1, 2)
print(point.description) //(1, 2)

클래스도 마찬가지로 간편하게 출력할 수 있습니다.

 

배열의 원소를 하나씩 출력할 때도

기존에는

for point in points {
    print("point: (\(point.x), \(point.y))")
}

이렇게 자주 썼잖아요?

 

이제

for point in points {
    print("point: \(point)")
}

이렇게 간편하게 출력할 수 있습니다.

 

감사합니다!


아직은 초보 개발자입니다.

더 효율적인 코드 훈수 환영합니다!

공감 댓글 부탁드립니다.

 

 

반응형