Как использовать ObjectIdentifier для хэширования UIView
Что проверяют
Понимаете ли вы разницу между идентичностью объекта и равенством по содержимому, и знаете ли, как это устроено в Swift. Следом часто спрашивают, зачем вообще ObjectIdentifier, если UIView и так можно положить в Set.
Как отвечать
ObjectIdentifier это Hashable-обёртка над адресом экземпляра класса (или метатипа). Два идентификатора равны тогда и только тогда, когда указывают на один и тот же объект. Типичные сценарии:
- ключ словаря:
[ObjectIdentifier: CGFloat]для кеша высот или состояния анимаций конкретной view. Ключ строится какObjectIdentifier(view); - свой
Hashableпо идентичности: вhash(into:)пишетеhasher.combine(ObjectIdentifier(self)), а в==сравниваете через===. Так делают для классов, которые не наследуютNSObject; - ключ по типу:
ObjectIdentifier(MyCell.self)удобен для реестра ячеек или DI-контейнера.
Для самой UIView важная оговорка: она наследник NSObject, а тот уже Hashable через hash и isEqual(_:), по умолчанию тоже по идентичности. Поэтому Set<UIView> работает и без обёрток, а ObjectIdentifier нужен, когда вы не хотите зависеть от переопределённого isEqual или храните view за протоколом с ограничением AnyObject.
Добавьте про время жизни: ObjectIdentifier не удерживает объект. Если view освободилась, её адрес может занять новый объект, и старый ключ в словаре внезапно совпадёт с чужой view. Храните рядом weak-ссылку или чистите словарь при удалении view.
Где спотыкаются
Используют hashValue как уникальный идентификатор. Хэш годится только для раскладки по корзинам: коллизии возможны, а в Swift хэширование рандомизировано и значения меняются между запусками. Уникальность даёт сравнение идентификаторов, а не хэшей.
Забывают про переиспользование адреса после освобождения и получают редкие, плохо воспроизводимые баги в кешах.
Путают идентичность с равенством: две view с одинаковым фреймом и цветом дадут разные ObjectIdentifier, и это ожидаемое поведение, а не ошибка.