Classes and constructs are a common and flexible construct for building code. We can use exactly the same syntax rules to define attributes (variables, constants) and add methods for classes and structures. Thus extending the functionality of classes and structures.

Unlike other programming languages, Swift does not require you to create separate interfaces and implementation files for custom classes and constructs. All you have to do is define a class or structure in a single file, and the system will automatically generate external interfaces for other code.

Note: Usually the forces of a class are called objects. In Swift, classes and constructs are more closely related than in other languages, and some of the functionality discussed in this chapter can be on classes and constructs. So the main use example. The content includes class and struct comparison, struct and enumeration are value types, class is reference type, selection of class and struct, assignment and copy behavior of string, array, and dictionary types


Class and structure comparison

Classes and structures in Swift have a lot in common:

  • Define attributes to store values
  • Define methods to provide functionality
  • Define subscript operations to access their values through subscript syntax
  • Define the constructor used to generate initialization values
  • Extend to add functionality implemented by default
  • Follow protocols to provide some standard functionality

Classes have the following additional capabilities compared to constructs:

  • Inheritance allows a class to inherit the characteristics of another class
  • Type conversions allow the type of an instance of a class to be checked and interpreted at run time
  • A destructor allows an instance of a class to free any resources it has allocated
  • Reference counting allows multiple references to a class

Note: Constructs are always passed through the code by being copied, without reference counting.

Define the grammar

Classes and structures are defined in a similar way. Class and struct are represented by the keywords class and struct, respectively, and their contents are defined in curly braces:

class SomeClass {
	// Define the class here
}
struct SomeStructure {
	// Define the structure here
}
Copy the code

Note: Each time you define a new class or structure, you are actually defining a new Swift type. Therefore, use UpperCamelCase (SomeClass, SomeStructure, etc.) to meet the standard Swift uppercase naming style (String, Int, Bool). Instead, use lowerCamelCase to name attributes and methods (such as Framerate and incrementCount) to distinguish them from the type name.

Here is an example of defining a structure and defining a class:

struct Resolution {
	var width = 0
	var height = 0
}
class VideoMode {
	var resolution = Resolution(a)var interlaced = false
	var frameRate = 0.0
	var name: String?
}
Copy the code

In the example above we define a structure called Resolution that describes the pixel Resolution of a display. This structure contains two storage properties named width and height. Stored properties are constants or variables that are bundled and stored in a class or structure. When these two attributes are initialized to the integer 0, they are inferred to be of type Int.

In the example above we also defined a class called VideoMode that describes a specific mode for a video display. This class contains four variables to store properties. The first is Resolution, which is initialized as an instance of a new Resolution structure, and the attribute type is inferred to be Resolution. The new VideoMode instance will also initialize three other attributes, namely, Interlaced, which is initialized to false, frameRate, which has an initial value of 0.0, and Name, which has the optional value String. The name property will be assigned to nil, which is optional.

Class and struct instances

The definition of the Resolution structure and VideoMode class only describes what Resolution and VideoMode are. They do not describe a specific resolution and video mode. To describe a particular resolution or video mode, we need to generate an instance of them.

let someResolution = Resolution(a)let someVideoMode = VideoMode(a)Copy the code

Both structures and classes use constructor syntax to generate new instances. In its simplest form, constructor syntax is followed by a pair of empty parentheses after the type name of a structure or class, such as Resolution () or VideoMode (). Any class or struct instance created in this way will have its properties initialized to default values.

Property access

By using dot syntax, you can access the properties of the instance. The syntax is that the instance name is followed by the attribute name:

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

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

someVideoMode.resolution.width = 1280
print("The width of someVideoMode is now \(someVideoMode.resolution.width)")
// Print "The width of someVideoMode is now 1280"
Copy the code

Note: Unlike the OC language, Swift allows you to set the child properties of a structure property directly. Resolution (someVideoMode) resolution (width); resolution (someVideoMode);

Member by member constructor of a structure type

All structures have an automatically generated member-by-member constructor that initializes the attributes of the members in the new structure instance. The initial value of each attribute in the new instance can be passed to the member-by-member constructor by the attribute name, as follows:

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

Unlike structs, class instances do not have a default member-by-member constructor

Structs and enumerations are value types

When a value type is assigned to a variable, constant, or passed to a function, its value is copied.

In Swift, all the basic types: integers, floating-point numbers, bools, strings, arrays, and dictionaries are value types and are implemented as structures at the bottom. All struct and enumeration types are value types. This means that their instances, and any value type attributes they contain, are copied as they pass through the code.

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

In the above example, we declare a constant named HD, whose value is an instance of Resolution initialized to a full HD video Resolution (1920 pixels wide and 1080 pixels high).

In order to meet the requirements of digital cinema (2048 pixels wide and 1080 pixels high), the width property of Cinema needs to be modified as follows:

cinema.width = 2048
Copy the code

Here, it will show that the width property of Cinema has indeed been changed to 2048:

print("cinema is now  \(cinema.width) pixels wide")
Print "Cinema is now 2048 pixels wide"
Copy the code

However, the initial HD instance has the width attribute of 1920:

print("hd is still \(hd.width) pixels wide")
// Print "HD is still 1920 pixels wide"
Copy the code

Prove that when you assign HD to cinema, you actually copy all the values stored in HD, and then store the copied data into a new cinema instance. Since the two are independent, changing the width of cinema to 2048 does not affect the value of width in HD.

Enumerations follow the same code of conduct:

enum CompassPoint {
	case North.South.East.West
}
var currentDirection = CompassPoint.West
let rememberedDirection = currentDirection
currentDirection = .East
if rememberedDirection == .West {
	print("The remembered direction is still .West")}// Print "The remembered direction is still.West"
Copy the code

Classes are reference types

Unlike value types, when a reference type is assigned to a variable and a constant is passed to a function, its value is not copied. Therefore, you refer to the existing instance itself rather than a copy of it. The following is an example of using VideoMode:

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)")
// Print "The frameRate Property of The eighty is now 30.0"
Copy the code

In the example above, a constant called tenEighty is declared that references a new instance of VideoMode. In the previous example, this video mode was given a copy (that is, an HD instance) of HD resolution (1920*1080). Also set to Interlaced and name it “1080I.” Maximum frame rate is 25.0 frames per second. TenEighty is then given a new constant called alsoTenEighty and the frame rate is modified for alsoTenEighty.

Because the class is a reference type, tenEight and alsoTenEight actually refer to the same VideoMode instance. In other words, they are two names for the same instance.

By looking at the frameRate property of tenEighty, we can see that it correctly displays the new frameRate of the referenced VideoMode instance, which is 30.0.

Note: tenEighty and alsoTenEighty are declared as constants rather than variables. However, you can still change tenEighty. FrameRate and alsoTenEighty. FrameRate because the values of tenEighty and alsoTenEighty do not change. They do not “store” the VideoMode instance, but merely refer to it. So, you change the frameRate property of the referenced VideoMode, not the value of the constant referencing the VideoMode.

The identity operator

Because classes are reference types, it is possible for multiple constants and variables to reference the same class instance simultaneously behind the scenes. (This is not true for structures and enumerations. Because they are value types, their values are always copied when given twice to constants, variables, or functions.

Swift has two identity symbols: equivalent to (===) and not equivalent to! == to determine whether two constants reference the same class instance.

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

Note: Equal to (==) and equivalent to (===) have different meanings:

Equal to indicating that the values of two instances are equal or the same is equivalent to indicating that constants or variables of two class types refer to the same class instance.

Pointer to the

In OC, Pointers are used to refer to an address in memory. A Swift constant or variable that refers to an instance of a reference type is similar to an OC pointer, but does not directly point to a memory address, and does not require the use of (*) to indicate that you are creating a reference. These references to other constants or variables in Swift are defined in the same way.

Class and struct selection

In our code, we can use classes and structs to define our custom data types.

However, struct instances are always passed by value and class instances are always passed by reference. This means they use different tasks. When you consider the data and functionality of an engineering project, you need to decide whether each data structure is defined as a class or a structure.

As a general rule, structures can be considered when one or more of the following conditions are met:

  • The primary purpose of this data structure is to encapsulate a small number of related simple data values
  • It is reasonable to expect that when an instance of the data structure is assigned or passed, the encapsulated data will be copied rather than referenced.
  • The value type properties stored in the data structure should also be copied, not referenced.
  • The data structure does not need to inherit the properties or behavior of another existing type.

Assignment and copy behavior for string, array, and dictionary types

In Swift, many basic types such as String, Array, and Dictionary are implemented as structures. This means that when assigned to a new constant, or when passed into a function or method, their values are copied.

The NSString, NSArray, and NSDictionary types in OC are implemented as classes, not constructs. They do not copy values when assigned or passed to functions or methods, but pass references to existing instances.