Как `NSAttibutedString` приравнивает значения атрибутов типа` Any`? - PullRequest
0 голосов
/ 22 ноября 2018

Метод enumerateAttribute(_:in:options:using:) для NSAttributedString, по-видимому, приравнивает произвольные экземпляры типа Any.Конечно, Any не соответствует Equatable, поэтому это не должно быть возможным.

Вопрос: Как метод сравнивает один экземпляр Any с другим?

Контекст: В Swift я создаю подкласс NSTextStorage, и мне нужно предоставить собственную реализациюэтот метод в Swift .

Наблюдения:

  • NSAttributedString атрибуты входят в пары ключ-значение, причем ключи являются экземплярами типа NSAttributedString.Key и значения являются экземплярами типа Any?, причем каждая пара связана с одним или несколькими диапазонами символов в строке.По крайней мере, так выглядит структура данных извне;внутренняя реализация непрозрачна.
  • Метод enumerateAttribute проходит по всему диапазону NSAttributedString, эффективно идентифицируя каждое различное значение, соответствующее указанному ключу, и с диапазонами, к которым применяется это значение.
  • Значения, соответствующие данному ключу, могут быть нескольких разных типов.
  • NSAttributedString, по-видимому, не имеет возможности узнать, какими базовыми типами могут быть значения Any, и, таким образом,, по-видимому, нет способа приведения типов для сравнения двух данных значений Any.
  • Тем не менее, метод каким-то образом различает диапазоны строки на основе различий в значениях Any.
  • Интересно, что метод способен различать значения, даже когда базовый тип не соответствует Equatable.Я полагаю, что это является подсказкой того, что метод может использовать какое-то отражение для сравнения.
  • Еще более интересным является то, что метод позволяет проводить различие между значениями, когда базовый тип соответствует Equatable, а разница между двумя значениями является той разницей, что конкретная реализацияEquatable намеренно игнорирует.Другими словами, даже если a == b возвращает true, если есть разница в непрозрачных свойствах a и b, которые игнорируются ==, метод будет обрабатывать значения как отличающиеся, а не одинаковые.
  • Я предполагаю, что метод соединяется с реализацией в ObjC.

Ответ: Это невозможно сделать в Swift?

1 Ответ

0 голосов
/ 22 ноября 2018

Как вы знаете, Cocoa - это Objective-C, поэтому это объекты NSDictionary Objective-C, а не объекты Swift Dictionary.Таким образом, для сравнения равенства используется Objective-C isEqual, а не Swift ==.Мы не связаны строгой типизацией Swift, протоколом Equatable или чем-то еще из Swift.

Для иллюстрации приведем медленную и глупую, но эффективную реализацию обнаружения запуска стиля:

let s = NSMutableAttributedString(
    string: "howdy", attributes: [.foregroundColor:UIColor.red])
s.addAttributes([.foregroundColor:UIColor.blue], 
    range: NSRange(location: 2, length: 1))
var lastatt = s.attributes(at: 0, effectiveRange: nil)
for ix in 1..<5 {
    let newatt = s.attributes(at:ix, effectiveRange:nil)
    if !(newatt as NSDictionary).isEqual(to: lastatt) {
        print("style run ended at \(ix)")
        lastatt = newatt
    }
}

Это правильно печатает:

style run ended at 2
style run ended at 3

Таким образом, поскольку всегда можно сравнить атрибуты в одном индексе с атрибутами в другом, можно реализовать перечисление атрибутов в Swift.(Является ли это хорошей идеей, другой вопрос.)

...