重要的一点是 Swift 在字符串上的 == 可能不等同于 Objective-C 的 -isEqualToString:。特殊之处在于 Swift 和 Objective-C 之间的字符串表示方式不同。
看看这个例子:
let composed = "Ö" // U+00D6 LATIN CAPITAL LETTER O WITH DIAERESIS
let decomposed = composed.decomposedStringWithCanonicalMapping // (U+004F LATIN CAPITAL LETTER O) + (U+0308 COMBINING DIAERESIS)
composed.utf16.count // 1
decomposed.utf16.count // 2
let composedNSString = composed as NSString
let decomposedNSString = decomposed as NSString
decomposed == composed // true, Strings are equal
decomposedNSString == composedNSString // false, NSStrings are not
NSString's 表示为 UTF-16 代码单元序列(大致读取为 UTF-16(固定宽度)代码单元数组)。而 Swift Strings 在概念上是“字符”序列,其中“字符”是抽象扩展字素簇的东西(读取字符 = 任意数量的 Unicode 代码点,通常用户视为字符和文本输入光标跳转的东西周围)。
接下来要提到的是 Unicode。关于它有很多要写的,但这里我们对一种叫做“规范等价”的东西感兴趣。使用 Unicode 代码点,视觉上相同的“字符”可以以不止一种方式编码。例如,“Á”可以表示为预先组合的“Á”或分解的 A + ◌́(这就是示例中 composed.utf16 和 decomposed.utf16 长度不同的原因)。值得一读的是this great article。
-[NSString isEqualToString:],根据documentation,逐个代码单元比较NSStrings代码单元,所以:
[Á] != [A, ◌́]
斯威夫特的字符串==compares characters by canonical equivalence。
[ [Á] ] == [ [A, ◌́] ]
在 swift 中,上面的示例将为字符串返回 true。这就是为什么-[NSString isEqualToString:] 不等同于 Swift 的 String ==。等效的纯 Swift 比较可以通过比较 String 的 UTF-16 视图来完成:
decomposed.utf16.elementsEqual(composed.utf16) // false, UTF-16 code units are not the same
decomposedNSString == composedNSString // false, UTF-16 code units are not the same
decomposedNSString.isEqual(to: composedNSString as String) // false, UTF-16 code units are not the same
另外,Swift 中的NSString == NSString 和String == String 之间也有区别。 NSString == 将导致 isEqual 和 UTF-16 代码单元逐个代码单元比较,而 String == 将使用规范等价:
decomposed == composed // true, Strings are equal
decomposed as NSString == composed as NSString // false, UTF-16 code units are not the same
还有整个例子:
let composed = "Ö" // U+00D6 LATIN CAPITAL LETTER O WITH DIAERESIS
let decomposed = composed.decomposedStringWithCanonicalMapping // (U+004F LATIN CAPITAL LETTER O) + (U+0308 COMBINING DIAERESIS)
composed.utf16.count // 1
decomposed.utf16.count // 2
let composedNSString = composed as NSString
let decomposedNSString = decomposed as NSString
decomposed == composed // true, Strings are equal
decomposedNSString == composedNSString // false, NSStrings are not
decomposed.utf16.elementsEqual(composed.utf16) // false, UTF-16 code units are not the same
decomposedNSString == composedNSString // false, UTF-16 code units are not the same
decomposedNSString.isEqual(to: composedNSString as String) // false, UTF-16 code units are not the same