需求:
对于数组或者 Data 等类型需要做一个类似切割的操作, 比如 123456, 想要在没两个字符后面添加 “:”, 结果是 12:34:56
方案有很多办法, 肯定是依赖 while 或者 for
现在通过系统的两个方法实现安全取值, 此方式比 sub 截取子段安全
一个是 prefix, 另一个是 dropFirst
prefix: 效果是获取从开始位置起固定长度的子段
dropFirst: 效果是获取从固定长度起到结束位置的子段
var data: String = "123456"
var result: String = ""
let interval: Int = 2
while data.isEmpty == false {
let prefix = data.prefix(interval)
let drop = data.dropFirst(interval)
result.append(contentsOf: prefix)
if drop.isEmpty == false {
result.append(contentsOf: ":")
}
data = String(drop)
}
print("data: \(data), result: \(result)")
通过 Collection 源码 可以知道 prefix 和 dropFirst 都是调用
func index(
_ i: Index, offsetBy distance: Int, limitedBy limit: Index
) -> Index?
prefix 和 dropFirst 源码:
@inlinable
public __consuming func prefix(_ maxLength: Int) -> SubSequence {
_precondition(
maxLength >= 0,
"Can't take a prefix of negative length from a collection")
let end = index(startIndex,
offsetBy: maxLength, limitedBy: endIndex) ?? endIndex
return self[startIndex..<end]
}
@inlinable
public __consuming func dropFirst(_ k: Int = 1) -> SubSequence {
_precondition(k >= 0, "Can't drop a negative number of elements from a collection")
let start = index(startIndex, offsetBy: k, limitedBy: endIndex) ?? endIndex
return self[start..<endIndex]
}
offsetBy:limitedBy: 方法源码中实际调用了 for 循环, 所以单独调用 prefix 和 dropFirst 就是两次 for, 为了满足需求并减少 for 可以自己模仿源码实现, 如图
最后把这一步可以抽成一个扩展:
extension Collection {
func split(maxLength: Int) -> (prefix: SubSequence, drop: SubSequence) {
let index = index(startIndex, offsetBy: maxLength, limitedBy: endIndex) ?? endIndex
return (self[startIndex..<index], self[index..<endIndex])
}
}
该方式的优点就是很安全不会crash