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/

最新文章

  1. 【ARL】Parallel Processing of Graphs
  2. 将Jquery序列化后的表单值转换成Json
  3. HDU 4901 The Romantic Hero
  4. 栈的的顺序实例SeqStack实现
  5. cocos2d-x 在xcode IOS模拟器中 开启IOS多点触控
  6. c++ builder TListView控件按字符串排序(根据网上代码亲测ok)
  7. 什么是ajax,ajax原理是什么 ,优缺点是什么
  8. poj3678
  9. 你好,C++(16)用表达式表达我们的设计意图——4.1 用操作符对数据进行运算
  10. Qt编程之QString 处理换行
  11. fuelSources
  12. python编码格式
  13. NEFUOJ 500 二分法+最大流量
  14. eclipse中运行出错:无法初始化主类的解决办法
  15. MySQL 事务机制
  16. 算法与cpu
  17. Golang面向对象编程-struct(结构体)
  18. eclipse背景色设置成护眼色(豆沙绿)
  19. linux 下 tomcat 运行报错 Broken pipe
  20. 接口没添加@responseBody注解

热门文章

  1. https和http的post发送总结
  2. SpringBoot-核心依赖说明
  3. 1、Bash Shell
  4. 5、vim编辑器
  5. sql语句 小记录
  6. FlowNet2.0论文笔记
  7. python常有模块:模块、引入语法、两种执行方式、模块搜索顺序
  8. Linux根文件系统和目录结构及bash特性3
  9. Windows下Redis如何永久更改密码
  10. linux 安装weblogic(转载)