ObjectIdentifier необходим для равенства Swift?

У нас есть несколько экземпляров пользовательского класса Swift, который наследуется от SKSpriteNode и может выполнить следующий код (грубо упрощенный для этого вопроса) правильно:

let instance1 = CustomClass()
let instance2 = CustomClass()
let instance3 = CustomClass()
let instance4 = CustomClass()

let array1 = [instance1, instance2]
let array2 = [instance3, instance4]

func check(testInstance: CustomClass) -> Bool {
   return array1.filter({ $0 == testInstance }).count > 0
}

check(testInstance: instance3)

Другими словами, выполнение check(testInstance: instance3) вернуло false, как ожидалось.

Однако мы сделали кучу изменений, а check перестали работать.

CustomClass не реализует протокол Equatable. Мы просто хотим обнаружить уникальные экземпляры.

Он только начал работать, когда мы использовали ObjectIdentifier, что означает, что функция изменилась на это:

func check(testInstance: CustomClass) -> Bool {
   return array1.filter({ ObjectIdentifier($0) == ObjectIdentifier(testInstance) }).count > 0
}

Почему требуется ObjectIdentifier и когда он должен использоваться для равенства объектов?

Это было написано с помощью Swift 3.

+1
источник поделиться
1 ответ

Зачем нужен ObjectIdentifier и когда он должен использоваться для равенства объектов?

Вам не нужно использовать ObjectIdentifier для выполнения сравнения идентичности в этом случае, вы можете просто использовать оператор идентификации === вместо этого, поскольку говорит Мартин здесь, для экземпляров класса эквивалентно использованию ObjectIdentifier == перегрузки:

func check(testInstance: CustomClass) -> Bool {
    return array1.contains(where: { $0 === testInstance })
}

Также обратите внимание, что мы используем contains(where:) over filter{...}.count > 0, поскольку первая будет замыкаться на замыкание при поиске соответствующего элемента, тогда как последняя оценивает всю последовательность (и создает ненужный промежуточный массив).

Прямое использование == для выполнения сравнения идентичности объектов могло работать из-за того, что CustomClass в конечном итоге наследует от NSObject, который соответствует Equatable, определяя перегрузка ==, которая вызывает isEqual(_:), который по умолчанию выполняет идентификацию сравнение.

Однако в целом на это нельзя полагаться - реализация isEqual(_:) может быть переопределена для выполнения сравнения, основанного на значениях свойств, а не на идентификаторе. Кроме того, семантически Equatable требует, чтобы реализация == основывалась на всех видимых аспектах (например, значениях свойств) сравниваемых экземпляров.

От документация:

Равенство влечет подменяемость - любые два экземпляра, которые сравнивают одинаково может использоваться взаимозаменяемо в любом коде, который зависит от их значения. Чтобы поддерживать замещаемость, оператор == должен учет всех видимых аспектов типа Equatable.

Поэтому использование == для сравнения идентичности в вашем классе никогда не было правильным, хотя оно, возможно, и сработало изначально.

Что касается того, когда следует использовать ObjectIdentifier, вам действительно не нужно его просто для сравнения идентичности. Для классов вам следует использовать оператор ===, а для метатипов вы должны просто использовать == перегрузку, определенную специально для них ( так как в этом случае идентичность просто происходит с равенством, так как каждый новый экземпляр метатипа уникален).

Основное использование ObjectIdentifier - это действительно его реализация hashValue, которая выводится из значения указателя, которое оно инициализировало. Это может быть полезно, например, для того, чтобы метатипы были Dictionary ключами (сравните Сделайте словарь Swift, где ключ имеет тип ",).

+2
источник

Посмотрите другие вопросы по меткам или Задайте вопрос