-1

enumerateAttribute(_:in:options:using:)方法NSAttributedString似乎等同于 type 的任意实例Any。当然,Any不符合Equatable,所以这应该是不可能的。

问题:该方法如何将一个实例与Any另一个实例进行比较?

上下文:在 Swift 中,我是 NSTextStorage 的子类,并且需要在 Swift中提供我自己的此方法的实现。

观察:

  • NSAttributedString属性以键值对的形式出现,键是 typeNSAttributedString.Key的实例,值是 type 的实例Any?,每一对都与字符串中的一个或多个字符范围相关联。至少,从外部看,数据结构是这样的;内部实现是不透明的。
  • enumerateAttribute方法遍历 a 的整个范围NSAttributedString,有效地识别与指定键对应的每个不同值,以及该值适用的范围。
  • 对应于给定键的值可以是多种不同的类型。
  • NSAttributedString似乎无法知道这些Any值可能是什么基础类型,因此似乎无法进行类型转换以比较两个给定Any值。
  • 然而,该方法以某种方式基于值的差异来区分字符串的范围Any
  • 有趣的是,即使底层类型符合Equatable. 我认为这是一个线索,表明该方法可能正在使用某种反射来执行比较。
  • 更有趣的是,该方法甚至在底层类型确实符合时区分值,Equatable并且两个值之间的差异是特定实现Equatable有意忽略的差异。换句话说,即使a == b返回 true,如果 a 和 b 被忽略的不透明属性存在差异==,该方法也会将值视为不同,而不是相同。
  • 我假设该方法连接到 ObjC 中的实现。

答案是: 不能在 Swift 中完成?

4

1 回答 1

0

如您所知,Cocoa 是 Objective-C,所以这些是 Objective-C NSDictionary 对象,而不是 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 中实现属性枚举。(这是否是一个好主意是另一个问题。)

于 2018-11-22T05:22:16.750 回答