📍 구조체 내 hash, == 메서드
struct TvResultItem: Hashable {
let tvResult: TvTMDBResult
let sectionType: TvSectionType
func hash(into hasher: inout Hasher) {
// tvResult.id와 sectionType을 조합해서 고유 해시 생성
hasher.combine(tvResult.id)
hasher.combine(sectionType)
}
static func == (lhs: TvResultItem, rhs: TvResultItem) -> Bool {
return lhs.tvResult.id == rhs.tvResult.id &&
lhs.sectionType == rhs.sectionType
}
}
✅ hash(into:) 메서드와 == 연산자의 역할
hash(into:)와 == 메서드는 Swift의 Hashable 및 Equatable 프로토콜을 구현할 때 필수적으로 고려해야 하는 요소입니다.
1️⃣ hash(into:) 메서드의 역할
hash(into:)는 객체의 해시값(Hash Value)을 생성하는 메서드입니다.
📌 역할
- hash(into:)는 Set, Dictionary, 그리고 DiffableDataSource와 같은 데이터 구조에서 객체를 빠르게 비교하고 구별할 때 사용됩니다.
- 특정 객체를 고유하게 식별할 때 해시값을 활용하며, 같은 해시값을 가지는 객체는 같은 것으로 인식됩니다.
- hash(into:) 메서드를 구현하면 어떤 기준으로 객체를 고유하게 식별할지 정의할 수 있습니다.
✅ 코드 예제 (TvResultItem에서 hash(into:) 역할)
func hash(into hasher: inout Hasher) {
hasher.combine(tvResult.id) // 기본적으로 같은 TV 프로그램은 같은 id를 가짐
hasher.combine(sectionType) // 하지만 다른 섹션이라면 다른 객체로 인식하게 함
}
💡 여기서 hasher.combine(sectionType)이 중요한 이유
- 같은 id를 가진 TvTMDBResult라도 섹션이 다르면 다른 객체로 취급하도록 하기 위해서!
- 두 객체는 id: 1001로 동일하지만, sectionType이 다름.
- hash(into:)를 올바르게 구현하면, 이 두 개가 다른 객체로 인식됨.
TvResultItem(tvResult: TvTMDBResult(id: 1001, name: "Breaking Bad"), sectionType: .airingToday)
TvResultItem(tvResult: TvTMDBResult(id: 1001, name: "Breaking Bad"), sectionType: .popular)
2️⃣ == 연산자 오버로딩 (Equatable)
📌 역할
- == 연산자는 두 개의 객체가 같은지 비교할 때 사용됩니다.
- 기본적으로 Hashable을 채택하면 Equatable도 자동으로 적용되지만, 기본 비교는 모든 속성이 동일한 경우에만 true를 반환합니다.
- 우리가 원하는 것은 "id가 같더라도 섹션이 다르면 다른 객체로 인식하는 것"이므로 직접 == 연산자를 구현하는 것이 중요합니다.
✅ 코드 예제 (TvResultItem에서 == 역할)
static func == (lhs: TvResultItem, rhs: TvResultItem) -> Bool {
return lhs.tvResult.id == rhs.tvResult.id &&
lhs.sectionType == rhs.sectionType // ✅ 같은 id라도 섹션이 다르면 다른 객체로 인식!
}
💡 왜 ==을 직접 구현해야 할까?
- Swift는 기본적으로 모든 프로퍼티가 같아야 동일한 객체라고 판단합니다.
- 하지만 우리는 tvResult.id와 sectionType만 비교하여, 같은 TV 프로그램이라도 다른 섹션이면 다른 객체로 취급되도록 해야 합니다.
3️⃣ hash(into:)와 ==이 없어도 동작하는 이유
💡 이 두 메서드가 없어도 정상적으로 동작하는 이유는?
- Swift의 Hashable 프로토콜은 자동으로 모든 프로퍼티를 기반으로 해시값을 생성합니다.
- 즉, hash(into:)를 직접 정의하지 않아도, Swift가 내부적으로 다음과 같이 처리합니다.
- Swift는 자동으로 tvResult 내부의 모든 프로퍼티를 해시값 계산에 사용합니다.
- 또한, == 연산자 역시 모든 프로퍼티가 같아야 true를 반환하도록 기본 구현됨.
func hash(into hasher: inout Hasher) {
hasher.combine(tvResult)
hasher.combine(sectionType)
}
✅ 그럼에도 불구하고 우리가 hash(into:)와 ==을 직접 정의하는 이유는?
- 최적화: 자동 생성된 hash(into:)는 모든 프로퍼티를 해싱하지만, 우리는 필요한 프로퍼티(id와 sectionType)만 해싱하여 성능을 최적화할 수 있음.
- 명확성: 우리가 비교하려는 핵심 요소(id와 sectionType)를 명확하게 지정할 수 있음.
- 예측 가능한 동작 보장: Swift의 자동 구현 방식이 내부적으로 변경될 가능성을 줄이고, 의도한 대로 정확한 비교를 수행하도록 보장.
4️⃣ hash(into:)와 ==을 만드는 방법
📌 직접 만들 때 고려해야 할 점
- 어떤 기준으로 객체를 고유하게 식별할지 정해야 함.
- 같은 객체로 취급해야 할 요소만 hash(into:)와 == 연산자에 포함해야 함.
✅ 기본 예제 (학생 데이터)
struct Student: Hashable {
let id: Int
let name: String
let grade: Int
func hash(into hasher: inout Hasher) {
hasher.combine(id) // ✅ id가 같으면 같은 학생으로 간주
}
static func == (lhs: Student, rhs: Student) -> Bool {
return lhs.id == rhs.id // ✅ id만 같으면 같은 학생으로 취급
}
}
✅ 고급 예제 (섹션 포함)
struct MovieItem: Hashable {
let movieID: Int
let section: String // ✅ 같은 영화라도 다른 섹션에 있을 수 있음
func hash(into hasher: inout Hasher) {
hasher.combine(movieID)
hasher.combine(section) // ✅ 같은 영화라도 다른 섹션이면 다른 객체로!
}
static func == (lhs: MovieItem, rhs: MovieItem) -> Bool {
return lhs.movieID == rhs.movieID &&
lhs.section == rhs.section
}
}
🛠️ hash(into:)와 ==을 구현해야 하는 경우
✅ 같은 id를 가진 객체라도 추가적인 구분이 필요할 때
✅ 해싱 기준을 최적화하고 싶을 때
✅ 특정 프로퍼티만 동일한지 확인하고 싶을 때
🛠️ hash(into:)와 ==을 구현하지 않아도 되는 경우
✅ 기본적인 동작(모든 프로퍼티 비교)이 우리가 원하는 동작과 일치할 때
✅ 모든 프로퍼티가 동일할 때만 같은 객체로 인식하고 싶을 때
💡 결론:
- hash(into:)와 == 연산자를 정의하면 같은 id라도 sectionType이 다르면 다른 객체로 인식할 수 있음
- 따라서 Diffable Data Source에서 중복 문제 없이 같은 TV 프로그램을 여러 섹션에서 보여줄 수 있음
- 기본적으로 없어도 동작은 하지만, 명확한 비교 기준을 직접 설정하고 성능을 최적화하려면 구현하는 것이 좋음
'Project > MovieClip' 카테고리의 다른 글
📍함수의 역할 -> configure<T: SelfConfiguringTVCell> () { } (0) | 2025.02.21 |
---|---|
🤔 영화 정보를 통합해서 관리할 필요가 있나? (0) | 2025.02.21 |
❌ 문제 발생... UICollectionViewDiffableDataSource.. 데이터 중복으로 인한 데이터 누락 (0) | 2025.02.20 |
⭐️ 버튼의 애니메이션 효과 주기 (0) | 2025.02.18 |
🤔 배우의 출연작 조회 (영화, 티비) (1) | 2025.02.18 |