美文网首页iOS
WWDC2016: 416 Understanding Swif

WWDC2016: 416 Understanding Swif

作者: 张宇航_Ken | 来源:发表于2016-07-07 21:05 被阅读382次

主要内容

讲解了Swift中对引用类型和值类型的内存管理方式,并提出一些优化建议。

影响性能的重要因素

  1. 栈内存 vs 堆内存
    栈内存的分配和释放仅仅是通过移动栈指针来实现的。而堆内存则要根据申请的大小在堆中寻找合适的位置,释放时需要将内存块回收到堆中,并且要考虑线程安全,所以会慢得多。Swift中的各种基本数据类型和容器(如Int, String, Array, Dictionary等)都是struct,编译器会尽可能用栈的方式来为它们分配内存。
  2. 引用计数
    编译器会自动在我们的代码中加入retainrelease的调用。这两个函数的调用频率极高且涉及线程安全的控制,使其有可能成为性能瓶颈。
  3. 动态函数调用 vs 静态函数调用
    动态函数调用就是在运行时才确定函数名对应的函数地址。对于动态调用,程序必须动态地查询函数地址,而无法在编译期使用inline等优化手段,会相对慢一些。

优化

struct作为Dictionary的key

Paste_Image.png

此处把3个数据点序列化为一个String,并以此为key。这样做有两个缺点:

  1. 使用String为key意味着可以传入任意String,哪怕不是这3个数据点的序列化结果,比如"abc"或"123"。
  2. String本身虽然是struct,但它内部储存字符的数组却是分配在堆上的。每次序列化意味着一次堆操作。
Paste_Image.png

改为struct可以优雅地解决这两个问题。

尽量使用(纯)值类型

Paste_Image.png

值类型如果包含引用类型的数据成员,则程序仍需为此数据成员进行引用计数的维护和堆操作。
所以,尽可能地使用“纯值”类型(这是我造的词,表示不包含引用类型的值类型,编译器可以实现完全的栈内存管理)。比如以下结构中,有两个String成员,程序需为它们进行堆操作。

Paste_Image.png

把它改成UUIDenum类型

Paste_Image.png

注意:此处的enum的源数据类型虽然是String,但是它在内存并不是持有一个String(虽然没说,我想应该只是个Int)。
所以整个结构变成了纯值类型。

感谢 小刚_aea8 的订正。此处的Attachment由于还包含了URL类型的成员,所以它并没有变成一个“纯值”类型。

在不需要多态时,使用泛型代替protocol

先说下多态的基本实现原理。

Paste_Image.png

上面的代码�是通过override的方式来实现多态的。编译器为每个对象添加了一个type成员,里面是这个具体类的信息(称为Virtual Method Table,简称V-Table),其中就包含了它override的函数指针,也就是各自的draw函数。下面调用的代码就是通过查询V-Table才找到正确的实现的。

Paste_Image.png

上面的代码把class改为了protocolstruct
像前一个例子中那样,class类型的数组的每个成员只占用一个�指针的size,因为只需放入一个指针。而这个例子中,由于struct是值类型,它被放进数组时应该是拷贝进去的,所以理论上,它要占用struct自身size的内存。那不同size的struct如何被放进同一个数组呢?
当需要用protocol类型指针来�指向struct时,Swift采用了一个叫Existential Container的结构来保存�struct的成员变量和方法。如下图:

Paste_Image.png

Existential Container的前3个word称为value buffer,是用来保存struct的数据成员的。对于比较小的struct可以直接把值塞进去,对于超过3个wordstruct,则只能分配在堆上,然后在这里保存一个指针。此时每个struct在栈上占用的空间就一样了(5个word),这就解答了上面的问题。接下来,为了统一处理不同size的struct,又在第4个word增加了一个叫Value Witness Table(简称VWT)的结构,里面包含了一组函数。如下图:

Paste_Image.png

PointLine为例:

函数 Point Line
allocate 没动作 在堆上分配内存,并保存指针到value buffer
copy 把值拷到value buffer 把值拷到value buffer中的指针对应的堆内存上
destruct 如果包含引用类型的成员变量,这里需要�引用减1。此处没有,所以没动作 也没动作
deallocate 没动作 释放堆上的内存

注:实际上不止这几个函数,还有allocateBufferAndCopyValueprojectBufferdestructAndDeallocateBuffer等。

Paste_Image.png

在第5个word上添加一个Protocol Witness Table(简称PWT)的结构。里面包含protocol的成员方法的指针。PWT与V-Table很类似,程序也是通过查询这个表来实现多态的。

Paste_Image.png

上图中,左上角是源代码,左下角是编译时产生的代码。可以看到:

  • 类型为Drawable的参数编译后变成ExistContDrawable,也就是上面提到的Existential Container。
  • 函数首先在创建一个ExistContDrawable类型的临时变量,用来放参数的值。
  • 拷贝type成员(里面包含实现类的信息,图中写错了,应该是 local.type = val.type)
  • 拷贝pwt成员(里面包含struct实现的protocol中的方法的函数地址)
  • 分配空间并赋值
  • 调用projectBuffer取出数据正确的内存地址(里面判断是否需要堆操作。我想应该是从前面的type里面取出实现类的size来判断的。)
  • 调用draw函数。声明中的draw方法虽然没有参数,编译出来后会加上一个参数,就是结构体的实际内存地址。
  • 最后调用destructAndDeallocateBuffer清理内存(temp写错了,应该是local)

一个简单的调用实际做了这么多事情。这些代价都是花在需要动态判断具体struct的信息和跳转到�对应的方法上的。如果改成使用泛型,则编译器就可以在编译期知道具体类型了,也就可以进行诸如inline等优化手段。具体实现�参考下面两张图:

用`protocol`实现 用泛型实现

但是,这样做的前提是不需要使用多态。如果像上面的例子那样,需要把不同的实现类放进一个数组中,则必须借用多态了。

使用“写时拷贝”

由于struct是值类型,当它在传递时会发生多次拷贝。如果你的struct拷贝成本很高或者拷贝发生得很频繁,而修改却很少的话,可以考虑使用“写时拷贝”的方法来优化它,如下:

Paste_Image.png

此处使用一个storageclass来包装数据。当Line发生拷贝时,storage成员只发生引用计数加一的操作。当需要真正写入时,再调用isUniquelyReferencedNonObjc判断一下storage的引用计数是否大于1, 是的话则显式拷贝一份再进行写入。
内置类型String,Array,Set,Dictionary均使用了这个技术。

相关视频

Paste_Image.png

相关文章

  • WWDC2016: 416 Understanding Swif

    主要内容 讲解了Swift中对引用类型和值类型的内存管理方式,并提出一些优化建议。 影响性能的重要因素 栈内存 v...

  • CHA1-Structure——5.理解App"资源&

    原文:Understanding App Resources —Understanding Strings and...

  • PlayGround

    WWDC2016 Introducing Swift Playgroundshttps://developer.a...

  • swif

    1. string拼接 swift中拼接字符串有两种方式1.使用+号进行两个字符串的相加,但是要都是非可选类型的字...

  • enum

    enum 枚举 @available & #available WWDC2016 Session笔记 - iOS ...

  • Swif学习之流程控制(if, switch,for,guard

    说明 以前一直用的是oc最近才开始学习swif,特记录一下swif的学习,主要记录下oc与swif的区别。 导入框...

  • Understanding

    If you love someone But can't get together with him or he...

  • The Understanding

    Everyone is eager to be understood, But in our life there...

  • WWDC2016

    今天早上打开苹果开发者网站,结果显示 我以为我打开了sublime,仔细一看,我怎么没写过这段代码,后来一看地址栏...

  • 学习switf 对象关联 objc_setAssociated

    swif版本 OC版本

网友评论

    本文标题:WWDC2016: 416 Understanding Swif

    本文链接:https://www.haomeiwen.com/subject/pnucjttx.html