Swift tutorial and the structure of such details

  • 2020-05-13 03:31:15
  • OfStack

Classes and structures are blocks of code that programmers often use in their code. Classes and structures can be implemented by defining properties and methods as well as constants, variables, and functions 1.

Unlike other programming languages, Swift does not need to create separate interfaces or implementation files to use classes or structures. Classes or structures in Swift can be defined directly in a single file, and once defined, can be used directly by other code.

Note: an instance of a class 1 is usually treated as an object, but in Swift, the class and structure are more like a functional method, and in subsequent chapters are more about the functionality of the class and structure.

1. Similarities and differences between classes and structures

Class and structure have some similarities, they can:

Define 1 attributes that can be assigned;

Define a functional method

Define subscripts, using subscript syntax

Define an initialization method to set the initial state

Extensibility over the original implementation method

Provides a specific category of basic functionality according to the protocol

You can read the sections on properties, methods, subscripts, initialization, extensions, and protocols for more

Class has a few other features that structures don't have:

Class inheritance

Real-time type conversion for class instances

Destruct an instance of a class to free up space

Reference counting, one instance of a class can have multiple references

You can read more about inheritance, type conversion, and initialization of automatic reference counting

Note: the structure copies the entire 1 every time it is passed through the code, so do not use reference counts

Define the grammar

Class and structure have a similar definition syntax, using the class keyword to define 1 class, and struct keyword to define the structure. Each definition consists of 1 pairs of braces:

 
class SomeClass {
// class definition goes here
}
struct SomeStructure {
// structure definition goes here
}

Note: when defining classes and structures, 1 generally USES the UpperCamelCase namespace to define class and structure names, such as SomeClass and SomeStructure, which also meets the standards of other types of Swift. When naming properties and methods, lowerCamelCase is usually used, such as frameRate, incrementCount, etc.
Here is an example of a structure and a class definition:

 
struct Resolution {
var width = 0
var height = 0
}
class VideoMode {
var resolution = Resolution()
var interlaced = falsevar
frameRate = 0.0
var name: String?
}

The above example first defines a structure called Resolution to describe the resolution of a pixel display, which has two properties called width and height. These two properties are defined as the Int type by default, and are initialized to 0.

Then a class called VideoMode is defined for the way the video is displayed. This class has four properties, the first of which, resolution itself, is a structure, followed by two more properties. The last attribute USES the optional string type String? , indicating that this property can exist or does not exist as nil.

Instances of classes and structures

The above two definitions only define the overall style of structure Resolution and class VideoMode. They themselves are not a specific resolution or display mode, so it is necessary to instantiate the structure and class.

The syntax of instantiation is similar:


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

Classes and structures are instantiated using the instance syntax. The simplest example syntax is done with two parentheses (). The properties in the instance defined in this case are initialized by default. You can refer to initialization 1 for more information.

To access attributes

You can easily access the properties of an instance using. Syntax. In. Syntax, add (.) after the instance name and the property name, no Spaces are required:


println("The width of someResolution is \(someResolution.width)")
// prints "The width of someResolution is 0"

In this example, someResolution.width represents the width property of someResolution, returning its initial value of 0

You can also use the. Syntax to continuously get the properties of an attribute, such as the width property of the resolution property in VideoMode

 
println("The width of someVideoMode is \(someVideoMode.resolution.width)")
// prints "The width of someVideoMode is 0"

Using this method, you can not only access, but also assign:

 
someVideoMode.resolution.width = 1280
println("The width of someVideoMode is now \(someVideoMode.resolution.width)")
// prints "The width of someVideoMode is now 1280"

Note: unlike Objective-C, Swift has the ability to directly set the children of a structure property, as in example 1 above.

A member initialization method for a structure type

Each structure has a member initialization method, which can be used to specify the initial value of each property by using the property name at the time of initialization:

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

Unlike structs, however, class instances cannot use member initializers, which are covered specifically in initialization 1.

2. Structure and enumeration types are numeric types

A value type means that when it is assigned to a constant or variable, or passed as a parameter to a function, a new value is copied in its entirety, rather than merely changing the reference object.

In fact, by this point you've already seen numeric types in the previous chapters, and all the basic types in Swift -- integer, floating point, Boolean, string, array, and dictionary -- are numeric types. They are also implemented by the structure.

All structure and enumeration types in Swift are numeric types. This means that every structure and enumeration you instantiate, and all the properties it contains, will be copied in full when passed in the code.

The following example illustrates this feature:

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

It declares a constant hd, which is an instantiation of Resolution, with a width of 1920 and a height of 1080, and then it declares a variable cinema, which is the same as hd. This time shows that cinema and hd are two instances, although they are both 1920 wide and 1080 high.

If you change the width of cinema to 2048, the width of hd will not change. It will still be 1920

 
cinema.width = 2048
println("cinema is now \(cinema.width) pixels wide")
// prints "cinema is now 2048 pixels wide"
println("hd is still \(hd.width) pixels wide")
// prints "hd is still 1920 pixels wide"

This indicates that when hd is assigned to cinema, a completely new Resolution structure is copied to cinema, so when cinema's properties are modified, hd's properties do not change.

The following example demonstrates an enumerated type:


enum CompassPoint {
case North, South, East, West
}
var currentDirection = CompassPoint.West
let rememberedDirection = currentDirection
currentDirection = .East
if rememberedDirection == .West {
println("The remembered direction is still .West")
}
// prints "The remembered direction is still .West"

Despite several assignments, rememberedDirection has not changed because the numeric type is copied completely during each assignment.

3. Class is the reference type

Unlike numeric types, a reference type does not copy the entire instance when it is assigned to another constant or variable, but instead creates a reference to an existing instance to represent it.

Here is an example of a reference where VideoMode is defined as a class:

 
struct Resolution {
var width = 0
var height = 0
}
class VideoMode {
var resolution = Resolution()
var interlaced = falsevar
frameRate = 0.0
var name: String?
}
0
Four properties of this instance tenEighty are initialized, and then tenEighty is assigned to another constant called alsoTenEighty, and frameRate of alsoTenEighty is modified

 
struct Resolution {
var width = 0
var height = 0
}
class VideoMode {
var resolution = Resolution()
var interlaced = falsevar
frameRate = 0.0
var name: String?
}
1
Since the class is a reference type, tenEighty and alsoTenEighty are actually the same instance, only with different names. We can prove this by checking frameRate:

 
println("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
// prints "The frameRate property of tenEighty is now 30.0"

Note that tenEighty and alsoTenEighty are defined as constants, not variables. But we can still change their property values, because they don't actually change themselves, they don't save the instance of VideoMode, they just reference one instance of VideoMode, and we change the properties in the instance that they reference.

Characteristics of the operation

Because a class is a reference type, there may be multiple constants or variables that only want an instance of one class (this is not true for numeric type structures and enums).

You can determine whether two constants or variables refer to an instance of the same class by following two operations:

Same instance (===)

Different instances (! = =)

Use these actions to check:

 
if tenEighty === alsoTenEighty {
println("tenEighty and alsoTenEighty refer to the same Resolution instance.")
}
// prints "tenEighty and alsoTenEighty refer to the same Resolution instance."

Note that the same example is judged using 3 consecutive equals, which is different from equality (two equals)

Instance sameness means that two variables or constants refer to instances of the same class

Equality means that two instances are numerically equal, or the same.

When you define a class, you need to say when two classes are equal and when two classes are not equal. More can be found in the equality operations 1 chapter.

Pointer to the

If you have programming experience with C, C++, or Objective-C, you will know that a pointer is used to refer to a memory address in these languages. A constant or variable that references an instance in Swift is similar to a pointer in C, but it is not a pointer to a memory address directly, nor is it necessary to use the * notation to indicate that you are defining a reference. References in Swift are defined in the same way as other variables and constants.

4. How do you choose to use classes or structures

You can select classes or structures in your code to implement the blocks you need to accomplish the corresponding functions. But the structure instance passes the value, while the class instance passes the reference. Then for different tasks, you should choose different instances, taking into account the different requirements of data structure and function.

1 generally speaking, you should choose to create a structure when one or more of the following conditions are met:

Structure is mainly used to encapsulate some simple data values

It is desirable that the encapsulated data be assigned rather than referenced when assigned or passed

All structed properties are themselves numeric types

The structure does not need to be inherited by another type or to complete any other behavior

Some examples of better use of structure:

The size of a geometry, which may include width, height, or other attributes, each of which is of type Double

The correspondence of 1 sequence may include the start start and length length attributes, each of which is of type Int

A point in the 3D coordinate system, including the coordinates x, y and z, is of type Double

In other cases, classes are a better choice. In other words, in the case of 1, custom 1 data structure 1 will be defined as a class.

5. Assignment and replication of collection types

In Swift, the array Array and the dictionary Dictionary are implemented using structures, but arrays differ from dictionaries and other structures when they are assigned or passed as arguments to functions.

And these operations of arrays and dictionaries, unlike NSArray and NSDictionary in Foundation, are implemented using classes.

Note: the following sections will cover copying arrays, dictionaries, strings, and so on. All of these replication operations appear to have occurred, but Swift will only fully replicate when it is needed to achieve optimal performance.

Dictionary assignment and copy operations

Each time a dictionary of type Dictionary is assigned to a constant or variable, or passed as a parameter to a function, the dictionary will not be copied until the assignment or function is called. This process is described in the section above: structures and enums are numeric types.

If the key values in the dictionary are numeric types (structures or enumerations), they are copied at the time of assignment. In contrast, if it is a reference type (class or function), the reference itself will be copied, not the class instance or the function itself. The dictionary is copied in the same way and in the same structure.

The following example shows a dictionary called ages, which stores a list of names and ages. When assigned to copiedAges, the values inside are copied in full. When the copied value is changed, the original value will not change, as shown in the following example:

 
struct Resolution {
var width = 0
var height = 0
}
class VideoMode {
var resolution = Resolution()
var interlaced = falsevar
frameRate = 0.0
var name: String?
}
4
The key of this dictionary is of type String, and the value is of type Int, both of which are numeric types, so they will be copied in full during assignment.

 
struct Resolution {
var width = 0
var height = 0
}
class VideoMode {
var resolution = Resolution()
var interlaced = falsevar
frameRate = 0.0
var name: String?
}
5

Array assignment and copy operations

The assignment and copy of the array Array is more complex than that of the dictionary Dictionary type. The Array type, similar to the C language, copies the values of the array in full only when needed.

If you assign an array to a constant or variable, or pass it as an argument to a function, replication does not occur during assignment and function calls. The two arrays will share a sequence of elements, and if you change one of them, the other one will change as well.

In the case of arrays, replication only happens if you do one operation that might modify the length of the array. This includes splicing, adding or removing elements, and so on. When replication actually happens, it's like dictionary assignment and copy operation 1.

The following example demonstrates the assignment of an array:

 
struct Resolution {
var width = 0
var height = 0
}
class VideoMode {
var resolution = Resolution()
var interlaced = falsevar
frameRate = 0.0
var name: String?
}
6

The array a is assigned to b and c, and the same subscript is output:

 
struct Resolution {
var width = 0
var height = 0
}
class VideoMode {
var resolution = Resolution()
var interlaced = falsevar
frameRate = 0.0
var name: String?
}
7

If you change a value in a, you will find that the values in b and c will also change, because the assignment does not change the length of the array:

 
struct Resolution {
var width = 0
var height = 0
}
class VideoMode {
var resolution = Resolution()
var interlaced = falsevar
frameRate = 0.0
var name: String?
}
8
However, if you add a new element to a, you change the length of the array, and the actual copy occurs. If you change the value of the element in a again, the element in b and c will not change:

 
struct Resolution {
var width = 0
var height = 0
}
class VideoMode {
var resolution = Resolution()
var interlaced = falsevar
frameRate = 0.0
var name: String?
}
9
Set the array to be unique to 1

If you can set the array to only 1 before you modify it, that's fine. We can use the unshare method to copy the array itself into a one-only entity.

If multiple variables refer to the same array, you can use the unshare method to do "stand alone" once


let someResolution = Resolution()
let someVideoMode = VideoMode()
0
If you change the value of b again, the value of c will not be affected

 
b[0] = -105
println(a[0])
// 777
println(b[0])
// -105
println(c[0])
// 42

Check that both arrays share the same elements

Use the instance equality operator to determine if two arrays share elements (=== and! = = =)

Here's an example to determine if an element is Shared:

 
if b === c {
println("b and c still share the same array elements.")
} else {
println("b and c now refer to two independent sets of array elements.")
}
// prints "b and c now refer to two independent sets of array elements."

You can also use this to determine if two subarrays have elements in common:

 
if b[0...1] === b[0...1] {
println("These two subarrays share the same elements.")
} else {
println("These two subarrays do not share the same elements.")
}
// prints "These two subarrays share the same elements."

Forced array copy

This is done by calling the copy method of the array. This method will copy a full array into the new array.

In the following example the array called names will be copied to copiedNames in its entirety.

 
var names = ["Mohsen", "Hilary", "Justyn", "Amy", "Rich", "Graham", "Vic"]
var copiedNames = names.copy()

By changing the value of copiedNames, it can be verified that the array has been copied completely and will not affect the previous array:

 
copiedNames[0] = "Mo"
println(names[0])
// prints "Mohsen"

Note: if you are not sure if the array you need is independent, just use unshare. The copy method copies the entire array once, regardless of whether it is currently independent or not, even if the array is already unshare.


Related articles: