这是个好问题。答案是……复杂。从 UTF-8 转换为 UTF-16,或反之亦然,或转换为其他编码或从其他编码转换,都需要检查字符串,因为字符可以由多个代码单元组成。因此,如果您想在恒定时间内获得计数,它将归结为内部表示是什么。如果字符串在内部使用 UTF-16,那么可以合理地假设 string.utf16.count 将处于恒定时间,但如果内部表示是 UTF-8 或其他,则需要分析字符串以确定UTF-16 中的长度是。那么 String 内部使用的是什么?嗯:
https://github.com/apple/swift/blob/master/stdlib/public/core/StringCore.swift
/// The core implementation of a highly-optimizable String that
/// can store both ASCII and UTF-16, and can wrap native Swift
/// _StringBuffer or NSString instances.
这令人沮丧。内部表示可以是 ASCII 或 UTF-16,或它可以包装一个 Foundation NSString。人力资源管理系统。我们确实知道 NSString 在内部使用 UTF-16,因为这是 actually documented,所以这很好。所以这里的主要异常值是字符串存储 ASCII 的时候。可取之处在于,由于前 128 个 Unicode 代码点与 ASCII 字符集具有相同的值,因此任何 ASCII 字符 0xXX 都应该对应于 UTF-16 字符 0x00XX,因此 UTF-16 长度应该是ASCII 长度乘以 2,因此可以在恒定时间内计算。执行中是不是这样?让我们看看。
在the UTF16View source 中,没有count 的实现。看来count是继承自Collection's implementation,是通过distance()实现的:
public var count: IndexDistance {
return distance(from: startIndex, to: endIndex)
}
UTF16View's implementation of distance() 看起来像这样:
public func distance(from start: Index, to end: Index) -> IndexDistance {
// FIXME: swift-3-indexing-model: range check start and end?
return start.encodedOffset.distance(to: end.encodedOffset)
}
在the String.Index source 中,encodedOffset 看起来像这样:
public var encodedOffset : Int {
return Int(_compoundOffset >> _Self._strideBits)
}
_compoundOffset 似乎是一个简单的 64 位整数:
internal var _compoundOffset : UInt64
而_strideBits 似乎也是一个直整数:
internal static var _strideBits : Int { return 2 }
所以...看起来...您应该从string.utf16.count 获得恒定的时间,因为除非我在某处犯了错误,否则您只是移位几个整数然后比较结果(我可能仍然会运行一些测试来确定)。当然,需要注意的是,这没有记录在案,因此将来可能会发生变化——特别是因为 documentation for String 确实声称它需要遍历字符串:
与 isEmpty 不同,计算视图的 count 属性需要遍历字符串的元素。
话虽如此,您使用的是UITextView,它是在Objective-C 中通过NSAttributedString 实现的。如果您愿意承担 Objective-C 消息传递开销(老实说,这可能会在场景下发生以生成 String),您可以调用它的 length 属性,因为NSAttributedString 建立在 NSString 之上,确实保证它在内部使用 UTF-16,几乎可以肯定是恒定时间。