➽ 08 structures and classes

Keywords: Swift

Compare structs and classes
Structures and classes in Swift have a lot in common. Both can:
● define attributes to store values
● define methods to provide functionality
● define sub subscripts to provide access to their values using subscript syntax
● define the initializer to set its initial state
● extend its functionality beyond the default implementation
● comply with the protocol to provide a standard function

Class has other functions that the structure does not have:
● inheritance enables one class to inherit the characteristics of another class.
● type conversion enables you to check and interpret the types of class instances at run time.
● Deinitializers enable instances of classes to release any resources that have been allocated.
● reference counting allows multiple references to class instances.

Class supports additional functionality at the expense of increased complexity. As a general guideline, we prefer structures because they are easier to reason and use classes when appropriate or necessary. In fact, this means that most of the custom data types you define are structures and enumerations.

Define syntax

struct SomeStructure {
    // structure definition goes here
}
class SomeClass {
    // class definition goes here
}
struct Resolution {
    var width = 0
    var height = 0
}
class VideoMode {
    var resolution = Resolution()
    var interlaced = false
    var frameRate = 0.0
    var name: String?
}

The name attribute automatically specifies a default value of nil or "no name value" because it is an optional type.

Structs and class instances

let someResolution = Resolution()
let someVideoMode = VideoMode()

This creates a new instance of the class or structure and initializes all properties to their default values.

Access and modify properties

print("The width of someResolution is \(someResolution.width)")
// Prints "The width of someResolution is 0"

print("The width of someVideoMode is \(someVideoMode.resolution.width)")
// Prints "The width of someVideoMode is 0"

someVideoMode.resolution.width = 1280
print("The width of someVideoMode is now \(someVideoMode.resolution.width)")
// Prints "The width of someVideoMode is now 1280"

Structure members are initialized one by one

let vga = Resolution(width: 640, height: 480)

Unlike structs, class instances do not receive default member initializers.

Both structs and enumerations are value types
A value type is a type whose value is copied when assigned to a variable or constant or passed to a function.

In fact, all basic types in Swift integers, floating-point numbers, Boolean values, strings, arrays, and dictionaries are value types and are implemented behind the scenes as structures.

All structures and enumerations are value types in Swift. This means that any structure and enumeration instances you create and any value types they pass as properties are copied in code.

Collections defined by the standard library, such as arrays, dictionaries, and strings, use optimization to reduce the performance cost of replication. Instead of creating copies immediately, these collections share the memory of the storage elements between the original instance and any copies. If you modify a copy of the collection, the elements are copied before the modification. The behavior you see in your code always seems to copy immediately.

let hd = Resolution(width: 1920, height: 1080)
var cinema = hd

Because Resolution is a structure, a copy of the existing instance is created and the new copy is assigned to cinema. Although hd and cinema now have the same width and height, they are two completely different instances behind the scenes. This is also supported by the following examples:

cinema.width = 2048

print("cinema is now \(cinema.width) pixels wide")
// Prints "cinema is now 2048 pixels wide"

print("hd is still \(hd.width) pixels wide")
// Prints "hd is still 1920 pixels wide"

The behavior is the same for enumerations:

enum CompassPoint {
    case north, south, east, west
    mutating func turnNorth() {
        self = .north
    }
}
var currentDirection = CompassPoint.west
let rememberedDirection = currentDirection
currentDirection.turnNorth()

print("The current direction is \(currentDirection)")
print("The remembered direction is \(rememberedDirection)")
// Prints "The current direction is north"
// Prints "The remembered direction is west"

Class is a reference type
Unlike value types, when a reference type is assigned to a variable, constant, or passed to a function, its value is not copied. Therefore, a reference to an existing instance is used instead of a copy.

let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0

let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0

print("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
// Prints "The frameRate property of tenEighty is now 30.0"

ID validation operator
Because a class is a reference type, multiple constants and variables can reference a single instance of the same class behind the scenes. (this is not the case for structures and enumerations, because they are always copied when they are assigned to constants or variables or passed to functions.)

Sometimes it is useful to find out whether two constants or variables refer to exactly the same instance of a class. To achieve this, Swift provides two identification operators: = = = and==

Use these operators to check whether two constants or variables refer to the same instance:

if tenEighty === alsoTenEighty {
    print("tenEighty and alsoTenEighty refer to the same VideoMode instance.")
}
// Prints "tenEighty and alsoTenEighty refer to the same VideoMode instance."

Pointer
If you have experience in C, C + +, or Object-C, you probably know that these languages use pointers to refer to addresses in memory. A Swift constant or variable that references an instance of a reference type is similar to a pointer in C, but it is not a direct pointer to an address in memory, and there is no need to write an asterisk (*) to indicate that a reference is being created. Instead, these references are defined the same as any other constant or variable in Swift. The standard library provides pointer and buffer types, which can be used if you need to interact directly with pointers.

Posted by justbane on Thu, 14 Oct 2021 14:25:32 -0700