Swift中的写时复制

1. 深复制和浅复制

浅拷贝只是对指针的拷贝,拷贝后两个指针指向同一个内存空间。深拷贝不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针。

2. 写时复制

在 Swift 中,所有的基本类型,包括整数、浮点数、字符串、数组和字典等都是值类型,并且都以结构体的形式实现。那么,我们在写代码时,这些值类型每次赋值传递都是会重新在内存里拷贝一份吗?

答案是否定的,想象一下,假如有个包含上千个元素的数组,然后你把它 copy 一份给另一个变量,那么 Swift 就要拷贝所有的元素,即使这两个变量的数组内容完全一样,这对它性能来说是多么糟糕。

而这个优化方式就是 Copy-On-Write(写时复制),即只有当这个值需要改变时才进行复制行为。

2.1 写时复制定义

如果结构体中的引用在被改变的一瞬间时是唯一的话 (比如,没有声明另外一个变量引用的话),那么也不会有复制发生,内存的改变将在原地进行。如果不是,就需要先进行复制,然后对复制的值进行变化,而保持其他的持有者不受影响。

    var x = [1,2,4]
    var y = x
    x.append(5) //1,2,4,5
    y.removeLast() //1,2

这时,把x赋值给y时会发生复制。这时候两个数组的引用指向的是内存中的同一个位置。共享存储部分。 当改变x时这个共享会被检查到。 内存将会被复制出来。 我们就独立的改变了两个变量。 耗性能的元素复制操作只会在必要的时候发送。这个就叫做写时复制。

简单来说: 复制时用的是一个内存地址,当某一个集合改变时恰到好处的复制了一份出来。

2.2 写时复制优点

写时复制 允许复制数组和原数组共享同一个内存地址,直到其中之一发生改变。这样的设计使得值类型可以被多次复制而无需耗费多余的内存,只有在变化的时候才会增加开销。因此内存的使用更加高效。

2.3 写时复制注意点
  • 复制结构体变量。里面进行的浅复制。对象本身不会被复制。只有引用会被复制。

  • “写时复制” 这个特性在 Swift 中是属于 值类型 的,而不是 Struct 独有的特性,在 Swift 中,典型的有 struct,enum,以及 tuple 都是值类型。而平时使用的 Int, Double,Float,String,Array,Dictionary,Set 其实都是用结构体实现的,也是值类型。

3. 代码示例


let array1 = [1, 2, 3, 4]
var array2 = array1
// 断点1
array2.append(2) 
// 断点2
print("hello world")
3. 1 断点1 处的调试
(lldb) fr v -R array1
(Swift.Array<Swift.Int>) array1 = {
  _buffer = {
    _storage = {
      rawValue = 0x0000000174264800 {
        Swift.__ContiguousArrayStorageBase = {
          Swift.__SwiftNativeNSArrayWithContiguousStorage = {
            Swift.__SwiftNativeNSArray = {}
          }
          countAndCapacity = {
            _storage = {
              count = {
                _value = 4
              }
              _capacityAndFlags = {
                _value = 8
              }
            }
          }
        }
      }
    }
  }
}
(lldb) fr v -R array2
(Swift.Array<Swift.Int>) array2 = {
  _buffer = {
    _storage = {
      rawValue = 0x0000000174264800 {
        Swift.__ContiguousArrayStorageBase = {
          Swift.__SwiftNativeNSArrayWithContiguousStorage = {
            Swift.__SwiftNativeNSArray = {}
          }
          countAndCapacity = {
            _storage = {
              count = {
                _value = 4
              }
              _capacityAndFlags = {
                _value = 8
              }
            }
          }
        }
      }
    }
  }
}
(lldb) po String(format: "%p", array1)
"0x170033ee0"

(lldb) po String(format: "%p", array2)
"0x174036e40"
*/

可以看出来 array1 和 array2 指向的内存地址都是 0x0000000174264800 ,其中 array1 自身地址 0x170033ee0 , array2 自身地址 0x174036e40

3. 2 断点2处的调试
(lldb)  fr v -R array1
(Swift.Array<Swift.Int>) array1 = {
  _buffer = {
    _storage = {
      rawValue = 0x0000000174264800 {
        Swift.__ContiguousArrayStorageBase = {
          Swift.__SwiftNativeNSArrayWithContiguousStorage = {
            Swift.__SwiftNativeNSArray = {}
          }
          countAndCapacity = {
            _storage = {
              count = {
                _value = 4
              }
              _capacityAndFlags = {
                _value = 8
              }
            }
          }
        }
      }
    }
  }
}
(lldb) fr v -R array2
(Swift.Array<Swift.Int>) array2 = {
  _buffer = {
    _storage = {
      rawValue = 0x00000001700ad620 {
        Swift.__ContiguousArrayStorageBase = {
          Swift.__SwiftNativeNSArrayWithContiguousStorage = {
            Swift.__SwiftNativeNSArray = {}
          }
          countAndCapacity = {
            _storage = {
              count = {
                _value = 5
              }
              _capacityAndFlags = {
                _value = 16
              }
            }
          }
        }
      }
    }
  }
}
(lldb) po String(format: "%p", array1)
"0x174036ea0"

(lldb) po String(format: "%p", array2)
"0x1700333a0"
*/

可以看出来 array1 指向的内存地址都是 0x0000000174264800 , array2 指向的内存地址都是 0x00000001700ad620 ,其中 array1 自身地址 0x174036ea0 , array2 自身地址 0x1700333a0

结束

本文完,睡觉去,miao~~~~

发布了19 篇原创文章 · 获赞 4 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/aluoshiyi/article/details/89504153
今日推荐