Some Additional Functionalities Of Swift – Part 3

Classes vs. Structures

Structure instances are always passed by value, and class instances are always passed by reference. This means that they are suited to different kinds of tasks.
As a general guideline, consider creating a structure instead of a class when one or more of these conditions apply:

1. The structure’s primary purpose is to encapsulate a few relatively simple data values.
2. It is reasonable to expect that the encapsulated values will be copied rather than referenced when you assign or pass around an instance of that structure.
3. Any properties stored by the structure are themselves value types, which you would also expect to be copied, as opposed to being referenced.
4. It is not necessary for the structure to inherit properties or behavior from another existing type.

For example, the size of a geometric shape would be a good choice for a structure, with the structure perhaps encapsulating a width property and a height property, both of type Double.

In all other cases, define a class. You will find that most custom data constructs should be defined as classes rather than as structures.
Swift’s String, Array, and Dictionary types are implemented as structures.

Modifying Value Types

Structures and enumerations are value types. By default, the properties of a value type cannot be modified from within its instance methods.
The mutating keyword is added to the method‘s definition so it can modify its properties.

struct Point {
 var x = 0.0, y = 0.0
 mutating func moveByX(dX: Double, dY: Double) {
   x += dX
   y += dY
 }
}

You cannot call a mutating method on a constant of structure type, because its properties cannot be changed.
Mutating methods can assign an entirely new instance to the implicit self property.

Type Methods

Instance methods are called on an instance of a particular type. A type method is called on the type itself, and is indicated by writing the keyword static before the method‘s func keyword:

class SomeClass {
 static func someTypeMethod() {
 // type method implementation goes here
 }
}
SomeClass.someTypeMethod()

As with instance methods, type methods are called with dot syntax. However, type methods are called on the type, not on an instance of that type.
Within the body of a type method, the implicit self property refers to the type itself, rather than an instance of that type.

Subscripts

Classes, structures, and enumerations can define subscripts, which are shortcuts for accessing the member elements of a collection, list, or sequence. Subscripts enable you to query instances of a type by writing one or more values in square brackets after the instance name.

Subscript definitions are written using the subscript keyword, and specify one or more input parameters and a return type, in the same way asinstance methods.
Here’s an example of a read-only subscript implementation, which defines a TimesTable structure to represent an n-times-table of integers:

struct TimesTable {
let multiplier: Int
 subscript(index: Int) -> Int {
   return multiplier * index
 }
}
let threeTimesTable = TimesTable(multiplier: 3)
print(threeTimesTable[5])
// prints "15"

Multiple subscripts can be defined for a single type. The appropriate subscript overload to use is selected according to the type of index value you pass to the subscript. Subscripts are not limited to a single dimension, and can be defined using multiple input parameters that best suit your custom type’s needs.

A Matrix is a good example how a subscript is used.
The following example defines a Matrix structure, which represents a two-dimensional matrix of Double values. The subscript takes two integer parameters:
struct Matrix {
 let rows: Int, columns: Int
 var grid: [Double]
 init(rows: Int, columns: Int) {
   self.rows = rows
   self.columns = columns
   grid = Array(count: rows * columns, repeatedValue: 0.0)
 }
 subscript(row: Int, column: Int) -> Double {
   get {
    return grid[(row * columns) + column]
   }
   set {
    grid[(row * columns) + column] = newValue
   }
 }
}

Matrix provides an initializer that takes two parameters called rows and columns to create an array large enough to store values of type Double. Each position in the matrix is given an initial value of 0.0.
Initialization is described in detail in the coming lessons.
You can construct a new Matrix instance by passing an appropriate row and column count to its initializer:

var matrix = Matrix(rows: 2, columns: 2)

This results in the following grid:


Values in the matrix can be set by passing comma-separated row and column values into the subscript.:
matrix[0, 1] = 1.5
matrix[1, 0] = 3.2


This results in the following:

Overriding

You can prevent a method, property, or subscript override by marking it as final (such as final var, final func, final class func, and final subscript).
You can mark an entire class as final by placing the final modifier before the class keyword in its class definition (final class).

Initializers

In the case of structure types that have no defined custom initializers, Swift automatically provides a memberwise initializer, even if the structure types have stored properties that do not have default values.
The Size structure automatically receives an init(width:height:) memberwise initializer, which you can use to initialize a new Size instance:

struct Size {
   var width = 0.0, height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0)

Class InitializationAll stored properties of a class – including any properties inherited from its superclassmust be assigned an initial value during initialization. In other words, assign a default value or create an initializer for the properties of the class.
The structure from the above example would have the following form as a class with an initializer:

class Size {
   var width:Double, height:Double
   init(w:Double, h:Double) {
     width = w
     height = h
   }
}
let twoByTwo = Size(w: 2.0, h: 2.0)

Required Initializers

Write the required modifier before the definition of a class initializer to indicate that every subclass of the class must implement that initializer:

class SomeClass {
 required init() {
 // initializer implementation goes here
 }
}

You must also insert the required modifier before every subclass implementation of a required initializer. This indicates that the initializer requirement applies to further subclasses along the chain. Do not write the override modifier when overriding a required designated initializer:

class SomeSubclass: SomeClass {
 required init() {
 // subclass implementation goes here
 }
}

It’s not necessary to provide an explicit implementation of a required initializer if you can satisfy the requirement with an inherited initializer.

Deinitialization

A deinitializer is called immediately before a class instance is deallocated, and is useful when you work with your own resources.
For example, if you create a custom class to open a file and write some data to it, you might need to close the file before the class instance is deallocated.
Class definitions can have at most one deinitializer per class.
Rather than providing you with the ability to call a deinitializer, Swift automatically calls it, just prior to instance deallocation. Superclass deinitializers are inherited by their subclasses, and the superclass deinitializer is called automatically at the end of a subclass deinitializer implementation. Superclass deinitializers are always called, even in cases in which subclasses do not provide their own deinitializers.

The deinit keyword is used to write a deinitializer, which is similar to writing an initializer using the init keyword. The deinitializer does not take any parameters and is written with no parentheses:

deinit {
 // perform the deinitialization
}

Deinitializers are only available on class types.

Advertisements
Some Additional Functionalities Of Swift – Part 3