Chapter 24: Value Types and Reference Type

大綱

Reference types

  • Reference types use assign-by-reference.

Value types

Defining value semantics

  • 如何測試一個type是否支援value semantics

    One benefit of value semantics is that they aid local reasoningarrow-up-right, since to find out how a variable got its value you only need to consider the history of interactions with that variable”

When to prefer value semantics

  • Value semantics are good for representing inert, descriptive data

    • numbers, strings, and physical quantities like angle, length or color,

    • don’t forget mathematical objects like vectors and matrices, pure binary data and collections of such values,

    • and lastly, large rich structures made from such values, like media

  • Reference semantics are good for representing distinct items in your program or in the world.

    • For example: constructs within your program such as specific buttons or memory buffers; an object that plays a specific role in coordinating certain other objects; or a particular person or physical object in the real world.

Case 1: Primitive value types

  • Primitive value types like Int support value semantics automatically. This is because assign-by-copy ensures each variable holds its own instance

Case 2: Composite value types

  • Composite value types, for example struct or enum

  • a simple rule: A struct supports value semantics if all its stored properties support value semantics.

  • If the type is an enumeration, it’s analogous: the instance copy is defined to have the same enumeration member, and it is as if that member’s associated values are directly assigned from the associated values of the existing instance.

Case 3: Reference types

  • Reference types can also have value semantics.

  • The solution is straightforward: To define a reference type with value semantics, you must define it to be immutable

  • Many of the basic UIKit utility types adopt this pattern. For instance, consider this code handling a UIImage:

    • UIImage, along with many of the Cocoa types, are defined as immutable for this reason, because an immutable reference type has value semantics.

    • The UIImage type has dozens of properties (scale, capInsets, renderingMode, etc.), but since they are all read-only you can’t modify an instance.

Case 4: value types containing mutable reference types

  • The final case is mixed types: value types that contain mutable reference types

Copy-on-write to the rescue

  • 如何讓mixed types也支援value semantics,就是透過Copy-on-write

  • But to a client with internal access or higher, the type behaves just like a struct that has value semantics, with two properties accentColor and bucketColor.”

Recipes for value semantics

  • For a reference type (a class):

    • The type must be immutable, so the requirement is that all its properties are constant and must be of types that have value semantics.

  • For a value type (a struct or enum):

    • A primitive value type like Int always has value semantics.

    • If you define a struct type with properties, that type will have value semantics if all of its properties have value semantics.

    • Similarly, if you define an enum type with associated values, that type will have value semantics if all its associated values have value semantics.

  • For COW value types —struct or enum:

    • Choose the “value-semantics access level”, that is, the access level that’ll expose an interface that preserves value semantics.

    • Make note of all mutable reference-type properties, as these are the ones that spoil automatic value semantics. Set their access level below the value-semantics level.

    • Define all the setters and mutating functions at and above the value-semantics access level so that they never actually modify a shared instance of those reference-type properties, but instead assign a copy of the instance to the reference-type property.

Key points

  • Value types and reference types differ in their assignment behavior. Value types use assign-by-copy; reference types use assign-by-reference. This behavior describes whether a variable copies or refers to the instance assigned to it.

  • This assignment behavior affects not only variables but also function calls.

  • Value types help you implement types with value semantics. A type has value semantics if assigning to a variable seems to create a completely independent instance. When this is the case, the only way to affect a variable’s value is through the variable itself, and you can simply think about variables as if instances and references did not exist.

  • Primitive value types and immutable reference types have value semantics automatically. Value types that contain reference types, such as mixed types, will only have value semantics if they are engineered that way. For instance, they might only share immutable properties, or privately copy shared components when they would be mutated.

  • Structural sharing is when distinct instances refer to a common backing instance that contributes to their value. This economizes storage since multiple instances can depend on one large shared instance. But if one instance can modify the shared backing instance, it can indirectly modify the value of other instances, so that the distinct instances are not fully independent, undermining value semantics.

  • Copy-on-write is the optimization pattern where a type relies on structural sharing but also preserves value semantics by copying its backing instance only at the moment when it itself is mutated. This allows the efficiency of a reference type in the read-only case, while deferring the cost of instance copying in the read-write case.

  • Reference types also have value semantics if you define them to be fully immutable, meaning that they cannot be modified after initialization. To do this it suffices that all their stored properties are read-only and of types that themselves have value semantics.

Last updated