[Swift] Hashable

han·2024년 9월 6일

Swift

목록 보기
1/1
post-thumbnail

오랜만에 Swift UI 공식 튜토리얼을 보기 시작하다 Landmark라는 구조체를 작성하면서 Hashable 프로토콜에 대해 문서를 읽고 정리해봤다.

Hashable

Hashable은 Swift에서 해시 함수를 통해 해시 값으로 변환할 수 있게 해주는 프로토콜이다. 따라서 Hashable을 채택한 타입은 식별자 역할을 할 수 있고 Set, Dictionary에서 하나의 키가 될 수 있음을 의미한다.

public protocol Hashable : Equatable {
	var hashValue: Int { get }
    func hash(into hasher: inout Hasher)
}

추가로 Hashable 프로토콜은 Equatable 프로토콜을 채택하고 있는 것을 볼 수 있다.

Equatable

Equatable을 채택하는 유형은 == 연산자를 사용해 동일성을 비교하거나 != 연산자를 사용해 부등성을 비교할 수 있다. Swift 표준 라이브러리 대부분의 기본 유형은 Equatable을 준수한다.

기본 유형이라고 하면 Int, String, Double.. 등 비교가 가능한 모든 것들이라고 생각할 수 있다. 아무런 추가 작성 없이 간단하게 == 연산을 할 수 있는 이유가 바로 Equatable을 채택하고 있기 때문이다.

Hashable이 Equatable을 채택 하고있는, 어쩌면 할 수 밖에 없는 이유는 두 해시 값을 비교할 수 있어야하기 때문이 아닐까?

추가로 Flutter을 사용해서 개발경험이 있는 사람이라면 Equatable이라는 패키지를 자연스럽게 사용해봤을 수 있는데, Swift의 Hashable + Equatable과 비슷한 역할을 하고있다고 볼 수 있겠다.

Hashable 채택하기

위에서 기본 유형들은 Hashable을 기본적으로 채택하고 있다고 설명했다. 그러면 커스텀 유형은 어떻게 Hashable을 채택해야할까?

구조체(struct)

구조체의 경우 값타입이다. 즉, 그 자체로 값이기 때문에 name과 age가 같으면 같은 값으로 취급한다. 따라서 따로 hash함수나 ==연산자를 구현하지 않아도 바로 hash연산이 가능하기 때문에 따로 구현할 필요가 없다.

struct Person: Hashable {
	var name: String
    var age: Int
}

클래스(class)

클래스는 참조타입이기 때문에 인스턴스는 값이 아닌 메모리 주소를 가르키고 있다. 따라서 두 클래스를 비교하기 위한 == 연산자와 hash연산을 어떻게 할건지 구현을 해줘야한다. 각 인스턴스는 메모리 주소로 고유하게 식별이 가능하지만 클래스의 내용 따라 고유한 해시값을 제공하기 위함이다.

class Person: Hashable {
    var name: String
    var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.name = age
    }
    
    // Equatable
    static func == (lhs: Person, rhs: Person) -> Bool {
        return lhs.name == rhs.name && lhs.age == rhs.age
    }
    
    // Hashable
    func hash(into hasher: inout Hasher) {
        hasher.combine(name)
        hasher.combine(age)
    }
}

열거형(enum)

열거형은 연관값이 있는 경우와 없는 경우로 나뉜다. 연관값이 없는 경우에는 채탱하지 않아도 자동으로 구현되지만 연관값이 있는 경우에 Hashable을 채택하고 연관값의 타입까지 Hashable을 채택하고 있어야 한다.

// 연관값이 없음
enum Framework {
	case react,
}

// 연관값이 있음, String은 Hashable을 채택하고 있음
enum Framework: Hashable {
	case react(madeBy: String)
}
profile
크로스 플랫폼과 사랑에 빠진 개발자의 글 연습

0개의 댓글