Chapter 24: Value Types and Reference Type
大綱
Value types vs. reference types
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 reasoning, 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