Copy-On-Write in Swift
Premature optimisation is the root of all evil. But, there are moments where we need to optimise our code. Let’s see how to improve the performance of value type in Swift.
Introduction
A variable in Swift can be either reference or value type.
The most basic distinguishing feature of a value type is that copying — the effect of assignment, initialization, and argument passing — creates an independent instance with its own unique copy of its data
In this article, I explain an optimisation of this copying: the copy-on-write.
What Is Copy-On-Write?
Every time we assign a value type to another one, we have a copy of the original object:
let myString = "Hello"
var myString2 = myString // myString is copied to myString2
myString2.append(" World!")
print("(myString) - (myString2)") // prints "Hello - Hello World!"
If we copy just a plain String we may not have problems with the performance.
We may start having some troubles when we have Arrays with thousands of elements, and we copy them around our app. For this reason, the Array has a different way to copy, which is called copy-on-write.
When we assign an Array to another one, we don’t have a copy. The two Arrays share the same instance. In this way, we don’t have two different copies of a big Array and we can use the same instance, with a better performance. Then, just when one of the two Arrays change we have a copy.
Let’s see an example:
var array1 = [1, 2, 3, 4]
address(of: array1) // 0x60000006e420
var array2 = array1
address(of: array2) // 0x60000006e420
array1.append(2)
address(of: array1) // 0x6080000a88a0
address(of: array2) // 0x60000006e420
We can notice, in this example, that the two Arrays share the same address until one of them changes. In this way, we can assign array1 to other variables several times without copying every time the whole array, but just sharing the same instance until one of them changes. This is very useful for the performance of our App.
For your info, this is the function used to dump the address:
func address(of object: UnsafeRawPointer) -> String {
let addr = Int(bitPattern: object)
return String(format: "%p", addr)
}
Unfortunately, not all the value types have this behaviour. This means that, if we have a struct with a lot of information, we may need a copy-on-write to improve the performance of our App and to avoid useless copies. For this reason, we have to create this behaviour manually.
Manual Copy-On-Write
Let’s consider a struct User in which we want to use copy-on-write:
struct User {
var identifier = 1
}
We must start creating a class, with a generic property T, which wraps our value type:
final class Ref {
var value: T
init(value: T) {
self.value = value
}
}
We use class—which is a reference type—because when we assign a reference type to another one the two variables will share the same instance, instead of copying it like the value type.
Then, we can create a struct to wrap Ref:
struct Box {
private var ref: Ref
init(value: T) {
ref = Ref(value: value)
}
var value: T {
get { return ref.value }
set {
guard isKnownUniquelyReferenced(&ref) else {
ref = Ref(value: newValue)
return
}
ref.value = newValue
}
}
}
Since struct is a value type, when we assign it to another variable, its value is copied, whereas the instance of the property ref remains shared by the two copies since it’s a reference type.
Then, the first time we change value of one the two Box variables, we create a new instance of ref thanks to:
1
2
3
4
5
guard isKnownUniquelyReferenced(&ref) else {
ref = Ref(value: newValue)
return
}
In this way the two Box variable don’t share the same ref instance anymore.
isKnownUniquelyReferenced returns a boolean indicating whether the given object is known to have a single strong reference.
Here the whole code:
final class Ref {
var value: T
init(value: T) {
self.value = value
}
}
struct Box {
private var ref: Ref
init(value: T) {
ref = Ref(value: value)
}
var value: T {
get { return ref.value }
set {
guard isKnownUniquelyReferenced(&ref) else {
ref = Ref(value: newValue)
return
}
ref.value = newValue
}
}
}
We can use this wrapping like this:
let user = User()
let box = Box(value: user)
var box2 = box // box2 shares instance of box.ref
box2.value.identifier = 2 // Creates new object for box2.ref
Conclusions
An alternative to this approach is using an Array—instead of Box—to wrap the value type to copy on write. Unfortunately, the approach with the Array has some disadvantages. You can find more details in the Apple’s optimisation tips.
https://marcosantadev.com/copy-write-swift-value-types/
Copy-On-Write in Swift的更多相关文章
- swift内存管理:值类型与引用类型
Use struct to create a structure. Structures support many of the same behaviors as classes, includin ...
- HEC-ResSim原文档
HEC-ResSim Reservoir System Simulation User's Manual Version 3.1 May 201 ...
- Swift Array copy 的线程安全问题
Swift Array copy 的线程安全问题 NSArray 继承自 NSObject,属于对象,有 copy 方法.Swift 的 Array 是 struct,没有 copy 方法.把一个 A ...
- iOS代码规范(OC和Swift)
下面说下iOS的代码规范问题,如果大家觉得还不错,可以直接用到项目中,有不同意见 可以在下面讨论下. 相信很多人工作中最烦的就是代码不规范,命名不规范,曾经见过一个VC里有3个按钮被命名为button ...
- iOS开发系列--Swift进阶
概述 上一篇文章<iOS开发系列--Swift语言>中对Swift的语法特点以及它和C.ObjC等其他语言的用法区别进行了介绍.当然,这只是Swift的入门基础,但是仅仅了解这些对于使用S ...
- Swift和Objective-C混编注意事项
前言 Swift已推出数年,与Objective-C相比Swift的语言机制及使用简易程度上更接地气,大大降低了iOS入门门槛.当然这对新入行的童鞋没来讲,的确算是福音,但对于整个iOS编程从业者来讲 ...
- thrift:swift项目笔记
先声明:此swift不是Apple公司的那个swift开发语言,而是facebook的另一个开源项目. facebook的thrift IDL文件,如果默认用thrift -gen java生成jav ...
- Swift 学习中的一点体会,不断更新中。。。
随着Xcode 8的发布,swift 3.0终于来了.又有一大批api名字发生了变化.但是感觉3.0之后的变化应该会小些,因此再重新仔细学习一下. 1. 关于swift引入的Computed Prop ...
- 【iOS】在Swift中使用JSONModel
前言 首先所有的Model还是使用oc来写——看到这一句是不是想关网页了- - #,在swift里面直接写一直报错所以就将就用oc来写了,这里主要是分享一下搭配Alamofire使用的经验. 声明 欢 ...
- 使用Swift打造动态库SDK和DemoAPP时所遇到的(Xcode7.3)
使用Swift开发SDK的优点是,生成的SDK对于Obj-C或是Swift调用都不需要自己去建桥接文件,因为Swift的SDK打包时默认已经自动生成供OC调用的.h文件.OC调用时直接import,s ...
随机推荐
- Python库整理
库名称简介 Chardet字符编码探测器,可以自动检测文本.网页.xml的编码. colorama主要用来给文本添加各种颜色,并且非常简单易用. Prettytable主要用于在终端或浏览器端构建格式 ...
- zencart批量评论插件Easy Populate CSV add reviews使用教程
此插件在Easy Populate CSV 1.2.5.7b产品批量插件基础上开发,有1.3x与1.5x两个版本. zencart批量评论插件Easy Populate CSV add reviews ...
- html2canvas-html图片合成-canvas生成图片
作用 html2canvas可以通过纯JS对浏览器端经行截屏,但截图的精确度还有待提高,部分css不可识别,所以在canvas中不能完美呈现原画面样式 支持的浏览器 Firefox 3.5+ Goog ...
- windows安装PostgreSQL
犹豫了一小下,初学不在linux下安装sql,虽然说书上有,还是想记录一下,以后好找 入门的书籍是SQL基础教程第二版,图书馆搜刮来的,毕竟要还 下载页面 http://www.enterprised ...
- 【idea】idea 2018.2 for mac永久破解激活方法(亲测2099)
1. 下载安装idea: 2. 下载激活Jar包 链接:https://pan.baidu.com/s/1NaxYrDNi2eW66epjmk10dg 密码:aec5 3. 在访达中新建/Librar ...
- BZOJ1030 [JSOI2007]文本生成器[DP+AC自动机]
我学到现在才是初三学弟的水平..哭 这里相当于求长度为$m$的,字符集$\{A...Z\}$的且不包含任一模式串的文本串个数.这是一个典型的AC自动机匹配计数问题. 设$f_{i,j}$表示在AC自动 ...
- five rendering ideas 里获取csm的 shadow边界做 pcf
http://advances.realtimerendering.com/s2011/White,%20BarreBrisebois-%20Rendering%20in%20BF3%20(Siggr ...
- 原生 JS实现一个简单分页插件
最近做的一个 PC端的需求,这个需求中有一个小点,页面底部有一块列表区域,这个列表的数据量比较大,需要进行分页控制,切换页码的时候,发送一个 ajax请求,在页面无刷新的情况下,实现列表数据的刷新,所 ...
- TXNLP 20-33
文本处理的流程 # encoding=utf-8 import jieba import warnings # 基于jieba的分词 seg_list = jieba.cut("贪心学院专注 ...
- hive三种调用方式
一.hive -e ‘sql语句’ (shell命令) 适合比较短的sql语句调用,优点是可以直接在shell中调用静音模式 -S 在执行HiveQL过程中,不在显示器输出MR的执行过程hive -S ...