-
[SwiftUI] Hashable, Equatable, IdentifiableApple🍎/iOS 2024. 11. 10. 23:33
struct에 Hashable이 선언된 곳 안에 또다른 데이터 타입으로 Hashable 상속을 안해주면 Equatable error 가 뜬다.
Type 'Hike' does not conform to protocol 'Equatable’
이라는 에러로 시작해 글을 쓰게 됐다. 맨날 대충 찾아보고 긁어넣었던 이것들은 무엇일까?
Hashable이란?
hash 값을 생성할 수 있는 유형.
즉, String, Int, Double, Float, Bool, Set 값들이 전부 해시값으로 바꿀 수 있는 것들이다.따라서 Hashable 프로토콜을 채택한 집합은 전부 이 유형을 갖고 있어야한다.
설정 안한 열거형을 정의했을 때 자동으로 해시 가능성을 얻는다. 이러한 hash값을 사용하는 이유는, 탐색의 속도가 빨라서이다.
해시 테이블은 hash index를 사용해서 내가 원하는 값을 한번에 찾을 수 있기 때문에 더 빠르게 찾을 수 있다.
Hashable은 Equatable 프로토콜을 상속하므로 Equatable의 요구사항도 충족해야한다.상속받는 Equatable은?
public protocol Equatable { /// Returns a Boolean value indicating whether two values are equal. /// /// Equality is the inverse of inequality. For any values `a` and `b`, /// `a == b` implies that `a != b` is `false`. /// /// - Parameters: /// - lhs: A value to compare. /// - rhs: Another value to compare. static func == (lhs: Self, rhs: Self) -> Bool }
Equatable의 정의를 보면, ==, != 를 사용해 동등성 비교를 하는 프로토콜이다.
Swift의 기본 데이터 타입(Int, Double, Float,,,) 도 Equatable을 준수하고 있기 때문에 값 비교가 가능해지는 것이다.그럼 struct, enum, class와 같은 사용자 타입에서는 어떻게 될까?
struct Phone { var name: String } Phone(name: "iPhone") == Phone(name: "Zfilp") // Error : Referencing operator function '==' on 'Equatable' requires that 'Phone' conform to 'Equatable'
이렇게 이런 식으로 구조체를 비교하면 에러가 발생한다.
그러므로 struct, enum 의 경우 Equatable을 채택해주어야 비교가 가능해진다.struct Phone: Equatable { var name: String }
하지만 class의 경우, 아래와 같은 함수를 추가로 넣어줘야한다.
class Phone: Equatable { var name: String init(name: String) { self.name = name } static func == (lhs: Phone, rhs: Phone) -> Bool { return lhs.name == rhs.name } } Phone(name: "iPhone") == Phone(name: "Zfilp") // false
Identifiable 이란?
각 개체를 고유하게 식별하기 위해 사용하는 프로토콜이다.
Identifiable을 채택한 구조체/클래스는 id라는 고유 식별자를 통해 개체를 구분할 수 있어야한다.
주로 list나 데이터 바인딩을 할 때 많이 사용된다.protocol Identifiable { associatedtype ID: Hashable var id: Self.ID { get } }
위와 같이 정의 자체에 변수 id가 있기 때문에, 구현해야한다.
import SwiftUI struct User: Identifiable { let id = UUID() let name: String } let users = [ User(name: "Alice"), User(name: "Bob"), User(name: "Charlie") ] // View struct ContentView: View { let users: [User] var body: some View { List(users) { user in // id를 통해 각 원소를 구분하기 때문에 안전하게 표시 가능하다. Text(user.name) } } }
Identifiable을 사용하는 이유는?
주된 이유는 SwiftUI에서 데이터 컬렉션의 각 항목을 고유하게 식별하기 위해서이다.
List, ForEach 등의 뷰를 사용할 때 데이터가 고유하게 식별되지 않으면 UI가 예상대로 업데이트되지 않거나, 중복된 항목으로 인해 에러가 발생할 수 있기 때문.
Identifiable을 사용하면 SwiftUI가 항목을 고유하게 구분하고, 데이터가 변경될 때 해당 항목만 부분적으로 업데이트할 수 있도록 돕는 특징이 있다.그러므로 아래와 같은 경우 Identifiable을 채택해 사용한다.
- List, ForEach 같은 데이터 항목을 다룰 때
- 동적인 데이터로, 데이터가 자주 변경되는 경우
- 애니메이션을 사용할 때
하지만 꼭 필요한 것은 아니다.
ForEach(items.indices, id: \.self) { index in Text(items[index]) }
위와 같이 따로 지정해주고 사용할 수도 있다.
요약
- Hashable: 각 개체의 데이터에 해시값을 생성해 빠른 탐색이 가능하다. Equatable을 상속함.
- Equatable: 객체의 동등성을 비교할 수 있게 하며, ==, != 연산자를 사용해 값 비교가 가능하다.
- Identifiable: SwiftUI에서 각 항목을 고유하게 구분할 때 사용되며, List나 ForEach와 같은 반복 뷰에서 데이터를 안정적으로 관리할 수 있게 한다.
'Apple🍎 > iOS' 카테고리의 다른 글
[UIKit/스토리보드] IBOutlet weak & Collection은 왜 weak가 아닐까? (0) 2024.12.30 [iOS] Kingfisher & AppStorage로 이미지 다운로드 속도 개선하기 (0) 2024.11.24 [iOS/SwiftUI] NavigationStack을 왜 사용하는가? (0) 2024.09.04 [iOS] ARC란 무엇인가 & 메모리 누수 (2) 2024.08.23 [iOS/OS] 메모리 구조 (2) 2024.08.23