猫·仁波切

会研发的PM才是好OP.

Swift String Extension for Integer Index(扩展String使之可以支持数字Index和数字Range)

使用效果:

1
2
3
4
5
6
let foo: String = "Hello World 世界"
println("foo[13] \(foo[13])")
println("foo[-1] \(foo[-1])")
println("foo[-2] \(foo[-2])")
dump(foo[1..5], name: "foo[1:4]")
dump(foo[10...13], name: "foo[10:13]")

简单介绍下实现背后的思路和想法。

首先熟悉 String/Range 的一定知道这玩意 subscript 类型只能是 String.Index,也就是通过 String.startIndex 或者 .endIndex 获得。很不爽,实际使用场景里面大家直接把 String 当 Unicode 字符串搞。假定无风险好了。

sizeofValue 检查 aString.endIndex 发现大小是 32,猜测是 4x8 (64位)个整数或者指针,如果是我,索引值肯定放在这个结构的第一位,所以这就是 _Dummy 结构的由来(猜测+代码验证),就是为了和 String.Index 类型大小一致结构相似(反正我只关心第一个,其他的我用 _padding 补全)

然后用强制类型转换,然后操作这个值。

至于重载 subscript ,那啥。看书好了。

然后有人说字符串没有长度方法。。

countElements(aString) 标准库函数。可以用 extension 做到 String 里。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
extension String {
    struct _Dummy {
        var idxVal: Int
        var _padding: Int
        var _padding2: Int
        var _padding3: Int
    }
    subscript (i: Int) -> Character {
        var dummy: _Dummy = reinterpretCast(i >= 0 ? self.startIndex : self.endIndex)
        dummy.idxVal += i
        let idx: String.Index = reinterpretCast(dummy)
        return self[idx]
    }
    subscript (subRange: Range<Int>) -> String {
        var start: _Dummy = reinterpretCast(self.startIndex)
        var end = start
        start.idxVal = subRange._startIndex
        end.idxVal = subRange._endIndex
        let startIndex: String.Index = reinterpretCast(start)
        let endIndex: String.Index = reinterpretCast(end)
        return self[startIndex..endIndex]
    }
}

讨论内容在 SwiftChina. 其中有人给出了负数 range 的实现。

Comments