问题是即使String.Index 确实符合Comparable 协议,您仍然需要指定要使用public struct Range<Bound> where Bound : Comparable {} 的Range 类型
注意:由于NSString 使用UTF-16,请检查this 以及您提到的link,您的初始代码不能正确用于字符由多个 UTF-16 代码点组成。 以下是 Swift 3 的更新工作版本。
extension Range where Bound == String.Index {
init(_ range: NSRange, in string: String) {
let lower16 = string.utf16.index(string.utf16.startIndex, offsetBy: range.location)
let upper16 = string.utf16.index(string.utf16.startIndex, offsetBy: NSMaxRange(range))
if let lower = lower16.samePosition(in: string),
let upper = upper16.samePosition(in: string) {
self.init(lower..<upper)
} else {
fatalError("init(range:in:) could not be implemented")
}
}
}
let string = "❄️Let it snow! ☃️"
let range1 = NSRange(location: 0, length: 1)
let r1 = Range<String.Index>(range1, in: string) // ❄️
let range2 = NSRange(location: 1, length: 2)
let r2 = Range<String.Index>(range2, in: string) // fatal error: init(range:in:) could not be implemented
回答 OP 的评论:问题是 一个 NSString 对象编码一个符合 Unicode 的文本字符串,表示为 UTF-16 代码单元的序列。构成字符串内容的 Unicode 标量值最长可达 21 位。较长的标量值可能需要两个 UInt16 值来存储。
因此,像❄️这样的一些字母在 NSString 中占用了两个 UInt16 值,而在 String 中只占用了一个。当您将 NSRange 参数传递给初始化程序时,您可能希望它在 NSString 中正常工作。
在我的示例中,将 string 转换为 utf16 后,r1 和 r2 的结果是 '❄️' 和 致命错误。同时,您原始解决方案的结果分别是“❄️L”和“Le”。希望您能看到其中的不同。
如果您坚持使用解决方案而不转换为utf16,您可以查看the Swift source code 做出决定。在 Swift 4 中,您将初始化程序作为内置库。代码如下。
extension Range where Bound == String.Index {
public init?(_ range: NSRange, in string: String) {
let u = string.utf16
guard range.location != NSNotFound,
let start = u.index(u.startIndex, offsetBy: range.location, limitedBy: u.endIndex),
let end = u.index(u.startIndex, offsetBy: range.location + range.length, limitedBy: u.endIndex),
let lowerBound = String.Index(start, within: string),
let upperBound = String.Index(end, within: string)
else { return nil }
self = lowerBound..<upperBound
}
}