Swift 4:Strings引用计数以及如何计算

Nic*_*ell 6 string optimization performance reference-counting swift

此性能优化WWDC视频表明字符串是引用计数的,因为它们在堆上.这对使用Strings的结构的性能以及Swift 4中是否有某些变化有影响(现在Strings又是集合 - 带有写入时的副本).好奇如何证明这一点并获得实际计数. CFGetRetainCount - 不适用于字符串.

请参阅https://developer.apple.com/videos/play/wwdc2016/416/

在此输入图像描述

使用Swift 4.

acc*_*ews 4

Swift 字符串是没有引用计数的值类型。但是字符串包含的字符保存在引用类型容器存储内的堆中,并且具有引用计数。

这就是为什么 Swift Strings 与其他集合一样具有写时复制优化功能 -

在 Structs 中使用字符串以及任何其他引用类型对于性能来说并不是一个好主意,因为在每次对 Struct 本身进行赋值时,所有其他引用类型和字符串存储都会被保留。

当您有一个包含N 个引用类型的值类型时,在每次赋值/deinit 时,您需要N 个保留/释放。并且您将有值类型的复制开销。

但是,如果您定义一个包含N 个引用类型的引用类型,则在每次赋值/取消初始化时,您将只有1 个保留/释放操作。

对于经验:

struct Label {
    var text: String
    var font: UIFont
    func draw() { }
}

let label1 = Label(text: "Hi", font: font)
let label2 = label1
retain(label2.text._storage)
retain(label2.font)
// finished using label1
release(label1.text._storage)
release(label1.font)
// finished using label2
release(label2.text._storage)
release(label2.font)
Run Code Online (Sandbox Code Playgroud)

如果Label作为一个类来实现,那就是

class Label {
        var text: String
        var font: UIFont
        func draw() { }
}

let label1 = Label(text: "Hi", font: font)
let label2 = label1
retain(label2)
// finished using label1
release(label1)
// finished using label2
release(label2)
Run Code Online (Sandbox Code Playgroud)

另一方面,这种方法与 struct 的线程安全性相冲突。相同的实例将在所有副本之间共享。

由于保留/释放操作是在堆上进行的,并且它们必须是线程安全的,因此这些操作的成本相当大。

因此,如果您确实需要出色的性能,包括微观优化,并且想要明智地使用值类型,则应该考虑这种方法。

PS:Swift 4 没有改变这种方法,写时复制是另一种优化。仅当包含具有多个引用的引用类型的值类型发生变化时,它才会创建副本。