Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift 4: Are Strings reference counted & how to get that count

This performance optimization WWDC video suggests that strings are reference counted because they are on the heap. This has implications on the performance of structs with Strings and whether something has changed in Swift 4 (now that Strings are collections again - with copy on write). Curious how to prove this out and get an actual count. CFGetRetainCount - doesn't work on Strings.

See https://developer.apple.com/videos/play/wwdc2016/416/

enter image description here

Using Swift 4.

like image 232
Nick McConnell Avatar asked Sep 15 '17 21:09

Nick McConnell


People also ask

Is string a value type in Swift?

Strings Are Value TypesSwift's String type is a value type. If you create a new String value, that String value is copied when it's passed to a function or method, or when it's assigned to a constant or variable.

What is string interpolation in Swift?

You've seen how you can type values for strings directly into your code, but Swift also has a feature called string interpolation – the ability to place variables inside your strings to make them more useful.

How do I find the retain count in Swift?

You can always check the reference count of a variable by using the CFGetRetainCount function. Note: Reference counting is done only to manage memory for reference types (e.g., classes and closures).


1 Answers

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.

like image 123
accfews Avatar answered Oct 01 '22 01:10

accfews