Swift Strings are value types which doesn't have reference counting. But the characters that string contains are kept in heap inside a reference type container storage, and that has reference count.
This is why Swift Strings has copy on write optimization -like other collections-
Using Strings -and also any other reference type- inside Structs is not a good idea for performance, because, on each assignment of Struct itself, all other reference types and String storage is retained.
When you have a value type that contains N reference type, on each assignment/deinit you need N retain/release. And you will have copy overhead for value types.
But if you define a reference type that contains N reference types, on each assignment/deinit you will have just 1 retain/release operation.
For exp:
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)
If the Label was implemented as a class it would be
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)
On the other hand, this approach conflicts with struct's thread-safety offer. The same instances will be shared among all copies.
Since retain/release operations are on the heap and they have to be thread-safe, there is considerable cost for these operations.
So if you really need a great performance, including micro optimizations, and you want to use value types wisely, you should consider this approach.
PS: This approach is not changed by Swift 4, copy on write is another optimization. It creates a copy, only when value types that contains reference types with multiple references, are mutated.