Identifiable란?
Identifiable란 인스턴스의 유일성을 위해 ID 설정을 강제하는 프로토콜입니다.
예를 들어, 성별, 키, 외모 등 모든 것이 완벽하게 동일한 쌍둥이가 있다고 합시다.
Equtable의 관점에서 보면 이 둘은 모든 값이 같기 때문에 동일한 사람입니다.
하지만 Identifiable 프로토콜은 이 둘에게 서로 다른 주민등록번호를 제공해서 다른 사람으로 구분합니다.
주민등록번호가 바로 ID 역할을 하는 것이죠.
Identifiable의 코드 부분입니다.
유일성을 보장하기 위해 Hashable 프로토콜을 채택하고 있습니다.
ID는 아래와 같은 특성을 갖습니다.
- UUID와 같이 항상 고유하게 보장됩니다.
- 환경마다 지속적으로 고유합니다.
- 프로세스의 수명 동안 고유합니다.
- 개체의 수명 동안 고유합니다.
- 현재 컬렉션 내에서 고유성을 갖습니다.
Identifiable는 클래스 타입을 위한 기본 구현을 제공하고 있습니다.
즉 클래스에서는 직접 만든 커스텀 클래스여도 Identifiable이 자동으로(?) 채택되어 있습니다.
물론 id를 커스텀할 수도 있습니다.
Identifiable는 왜 사용할까?
구조체와 클래스를 선택하는 기준
Identifiable는 구조체와 클래스 중 무엇을 사용할지 선택하는 기준이 되기도 합니다.
Swift 레퍼런스 Structures and Classes의 내용 중 Identity Operators 단원에 관련 내용이 있습니다.
두 상수 또는 변수가 정확하게 동일한 클래스 인스턴스를 참조하는지 알아보기 위해 유용합니다.
구조체는 값 타입이기 때문에 항상 값을 복사하므로 위와 같은 비교가 불가능합니다.
ID를 강제할 때
어떤 struct나 class를 정의할때 ID를 강제하고 싶을 때 해당 프로토콜을 채택합니다.
Student 구조체를 정의할 때 학번은 필수이지만 개발자의 실수로 만들지 않을 수도 있습니다.
ID가 반드시 필요할 때 Identifiable를 채택하여 ID로 같음을 구분한다고 개념적으로 명시할 수 있습니다.
이 내용은 아래에서 직접 구현도 해보겠습니다.
Identifiable 사용해보기
class 비교
클래스 인스턴스 비교를 먼저 해보겠습니다.
class Person {
var name: String
init(name: String) {
self.name = name
}
}
let person1: Person = Person(name: "시리")
let person2: Person = person1
let person3: Person = Person(name: "시리")
Person 클래스를 작성했습니다.
person1, person3은 인스턴스를 각각 생성하였고 person2는 person1을 대입합니다.
if person1 === person2 {
print("same memory")
} else {
print("diff memory")
}
if person1 === person3 {
print("same memory")
} else {
print("diff memory")
}
위 코드의 결과는 어떻게 나올까요?
person1과 person2는 동일한 메모리를 참조합니다.
따라서 same memory가 출력됩니다.
하지만 person1과 person3은 서로 다른 메모리를 참조하기 때문에 diff memory가 출력됩니다.
이렇게 인스턴스가 동일한 메모리를 참조하는지 알고 싶을 때 유용합니다.
struct에서 Identifiable 사용하기
struct에서 Identifiable을 채택해서 사용해보겠습니다.
struct Person: Identifiable {
var name: String
}
struct 이름 옆에 Identifiable을 적어 프로토콜을 채택합니다.
위 코드처럼 Identifiable을 채택했을 때 id 변수를 만들지 않으면 아래처럼 에러가 발생합니다.
Identifiable을 채택하면 id 변수가 반드시 필요합니다.
그래서 id 선언을 강제할 때 프로토콜을 채택하면 좋습니다.
let person1: Person = Person(id: 0, name: "시리")
let person2: Person = Person(id: 1, name: "시리")
if person1.id == person2.id {
print("same")
} else {
print("diff")
}
//diff
심플한 예시입니다.
Person 인스턴스를 두 개 만들어 id를 다르게 설정합니다.
이 id를 이용해 같은지를 비교하면 객체가 같은지 다른지 알 수 있습니다.
위 예시에서 id가 없었다면 name만으로 비교를 해야하는데 둘 다 이름이 같기 때문에
제대로 된 비교가 불가했을 것입니다.
Identifiable를 채택하여 이 구조체는 id를 이용해 유일성을 판단한다는 것을 명시하고
id를 구현하여 인스턴스 비교를 가능하게 했습니다.
위 예시에서는 직접 id를 설정했기 때문에 유일성에서 별로 의미있는거 아닌가? 의아할 수 있습니다.
그럼 아래 코드를 봐보시죠.
struct Person: Identifiable {
var id: UUID = UUID()
var name: String
}
let person1: Person = Person(name: "시리")
let person2: Person = Person(name: "시리")
print("person1 id: \(person1.id)")
print("person2 id: \(person2.id)")
//person1 id: 1C59C9A0-6AB5-4B85-905C-F97F8208EDF9
//person2 id: 1775FE53-72DD-4D53-8B6D-DB2897A2393B
위 코드는 인스턴스가 생성되면 자동으로 id를 설정합니다.
id로 UUID를 사용하기 때문에 유일성도 충족하고 개발 편의성도 좋죠.
어떻게 사용해야할지 감이 오시나요??
Identifiable을 이용해 인스턴스 비교를 정확하고 편하게 해봅시다~
참고
https://developer.apple.com/documentation/swift/identifiable
https://nshipster.com/identifiable/
https://github.com/apple/swift-evolution/blob/main/proposals/0261-identifiable.md
https://seorenn.tistory.com/37
아직은 초보 개발자입니다.
더 효율적인 코드 훈수 환영합니다!
공감과 댓글 부탁드립니다.