There are two big data types in Swift, namely value type and reference type. In addition to simple data types, string, array, dictionary, structure and enumeration are all value types. Classes are of course reference types.

Swift provides a copy-on-write technique for value types. Let’s start with an example to illustrate this mechanism.

Example description:

  • Prints the memory address of the variable
Func printAddress(_ p: UnsafeRawPointer) {let addr = Int(bitPattern: p) print(String(format: "-> %p", addr)) }Copy the code
  • Array examples and execution results

    • Example:
    Var newList = list printAddress(&newList) var newList = list printAddress(&newList) PrintAddress (&list) printAddress(&list) printAddress(&newList)Copy the code
    • Execution Result:

    Note: even if the value of 1.3 is set to 3, the same as the original value, then the copy operation will be performed, you can verify for yourself.

  • Analysis:

    1. whennewList 在1.2The first assignment, the memory address andlsitIf yes, no operation is performedcopyOperation. Two arrays share memory space, which can save memory.
    2. whennewList 在1.3When you change the value of one of the elements, the memory address changes,listThe address hasn’t changed, so it follows that it’s donecopyOperation.
    3. This only happens in replicaschangeWhen toCopy operationIs calledWhen writing copy

Swift official documentation:

  • Structures and Enumerations Are Value Types

    A value type is a type whose value is copied when it’s assigned to a variable or constant, or when it’s passed to a function.

    You’ve actually been using value types extensively throughout the previous Chapters. In fact, All of the basic types in swift-integers, floating-point numbers, Arrays and dictionaries — are value types, and are implemented as structures behind the scenes.

    All structures and enumerations are value types in Swift. This means that any structure and enumeration instances you Create — and any value types they have as properties — are always copied when they’re passed around in your code.

    NOTE

    Collections defined by the standard library like arrays, dictionaries, and strings use an optimization to reduce the performance cost of copying. Instead of making a copy immediately, these collections share the memory where the elements are stored between the original instance and any copies. If one of the copies of the collection is modified, the elements are copied just before the modification. The behavior you see in your code is always as if a copy took place immediately.

  • The documentation makes it clear that all the basic types in Swift — integers, floating-point numbers, bools, strings, arrays, and dictionaries — are value types.

  • Collections defined by the standard library, such as arrays, use copy-on-write technology to reduce the performance cost of replication.

Verify official instructions:

So let’s verify that we’ve already verified arrays, now let’s verify basic single data types and custom structure types,

1. Basic types (Int, String, etc.)

  • Sample code:

    var a = 10
    printAddress(&a)
    
    var b = a
    printAddress(&b)
    
    var str = "hello Swift"
    printAddress(&str)
    
    var str2 = str
    printAddress(&str2)
    
    Copy the code
  • Execution Result:

  • Analysis:

    • As a result, this happens directly when you assign a value from one variable to anothercopyOperations do not consume much memory for these simple data types, so they are not neededCopy-On-WriteTechnology.

2. Custom Struct

  • The sample

    struct LGPerson {
        var name: String
    
        init(name: String) {
            self.name = name
        }
    }
    
    var Jack = LGPerson(name: "Jack")
    printAddress(&(Jack))
    
    var Andy = Jack
    
    printAddress(&(Andy))
    
    Copy the code
  • Execution Result:

  • Analysis:

    • As a result, as with primitive data types, assignment happens directlycopyOperation is not supported for custom constructsCopy-On-WriteTechnology.

Summary:

  1. Library defined collections (such as arrays, dictionaries) are usedCopy-On-Write, effectively reducing the memory overhead.
  2. Basic data types are not supportedCopy-On-Write, such as Int, String, struct, etc.
  3. This is the system default forAn array of.The dictionaryImplemented features.

How to useCopy-On-Write

If a structure has a member of type class, when an assignment is copied, does that member also get a copy of the new one? If not, how do we do that?

Verify that class members of structs are copied

  • Example:

    class Dog: NSObject {
    
        var age: Int
    
        init(age: Int) {
            self.age = age
        }
    }
    
    struct LGPerson {
        var name: String
        var dog: Dog
    
        init(name: String, dog: Dog) {
            self.name = name
            self.dog = dog
        }
    }
    
    let dog1 = Dog(age: 2)
    
    var Jack = LGPerson(name: "Jack", dog: dog1)
    print(Jack.dog.description,"\n")
    
    print("Jack-dog age: \((Jack.dog).age)\n")
    
    var Andy = Jack
    Andy.name = "Andy"
    print(Andy.dog.description,"\n")
    
    Andy.dog.age = 5
    
    print(Jack.dog.description,"\n")
    print(Andy.dog.description,"\n")
    
    print("Jack-dog age: \((Jack.dog).age)")
    print("Andy-dog age: \((Andy.dog).age)")
    
    Copy the code
  • Execution Result:

  • Analysis: Obviously,dog1 is not copied as Jack assigns a value to Andy. When andy.dog.age = 5 is executed, the value of Jack.dog.age also changes, which is obviously not what we want. What we want is for dog to be a new piece of data, preferably with a feature like copy-on-write. Copy only when needed.

Summary:

  • Struct class members will not becopy

Custom implementationCopy-On-Write

``` class Dog: NSObject { var age: Int init(age: Int) { self.age = age } } struct LGPerson { var name: String var dog: Dog init(name: String, dog: Dog) { self.name = name self.dog = dog } var dogAge: Int { get {return dog.age} set { if ! isKnownUniquelyReferenced(&dog) { dog = Dog(age: newValue) return } dog.age = newValue } } } let dog1 = Dog(age: 2) var Jack = LGPerson(name: "Jack", dog: dog1) print(Jack.dog.description,"\n") print("Jack-dog age: \((Jack.dog).age)\n") var Andy = Jack Andy.name = "Andy" print(Andy.dog.description,"\n") Andy.dogAge = 5 print(Jack.dog.description,"\n") print(Andy.dog.description,"\n") print("Jack-dog age: \((Jack.dog).age)") print("Andy-dog age: \((Andy.dog).age)") ```Copy the code
  • Execution Result:

  • Analysis:

    • You can see from the results that when you passAndy.dogAge = 5Changed thedog.ageAfter the value of,AndythedogThe member generates a new object. We got what we wanted. We got itCopy-On-WriteFeatures.
  • Note:

    • There are problems: but there is also a problem with usingAndy.dog.age = 5changeAndythedogMembers of theageProperty, the result is still not what we want, it’s going to be the same as when we first validated it,dogThe object is notcopy.
    • Solution: You candogThe member is set to read-onlyprivate(set) var dog: DogDon’t do this directly on your ownageProperties will do.

Custom implementationCopy-On-WriteAdvanced version

  • The read-only property above solves the problem, but it’s still not complete. Let’s look at a more complete and flexible approach.

  • Implement a generic value type T with copy-on-write property with a reference type, changing Dog to a structure.

    final class Ref<T> { var val: T init(_ v: T) {val = v} } struct Dog { var age: Int init(age: Int) { self.age = age } } struct LGPerson<T> { private var ref: Ref<T> var name: String init(name: String, _ ref: T) { self.name = name self.ref = Ref(ref) } var dog: T { get {return ref.val} set { if ! isKnownUniquelyReferenced(&ref) { ref = Ref(newValue) return } ref.val = newValue } } } let dog1 = Dog(age: 2) var Jack = LGPerson(name: "Jack", dog1) //print(Jack.dog.description,"\n") print("Jack-dog age: \((Jack.dog).age)\n") var Andy = Jack Andy.name = "Andy" //print(Andy.dog.description,"\n") Andy.dog.age = 5 //print(Jack.dog.description,"\n") //print(Andy.dog.description,"\n") print("Jack-dog age: \((Jack.dog).age)") print("Andy-dog age: \((Andy.dog).age)")Copy the code

The copy-on-write technology is widely used in Swift standard library.

This is actually an example from the documentation πŸ˜„πŸ˜„πŸ˜„

Advice: Use copy-on-write semantics for large values

# summary

  1. Copy-On-WriteIs a mechanism for optimizing copy operations on memory-heavy value types;
  2. forInt.Double.StringAnd other primitive types, which are copied when assigned;
  3. forArrayType, which does not copy when assigned, but only when modified;
  4. This is not implemented automatically for custom data typesCopy-On-Write, can be achieved according to their own actual situation.

Check whether Dictionary, Set, and Copy-On-Write are implemented by default.

I simply verified that it was a direct assignment, but many introductions say there is a copy-on-write feature implemented by default. You can verify, exchange, maybe my method is not correct.

String to be checked again,