Initializing and Deinitializing Data - Putting Expressions Together - Swift For Dummies (2015)

Swift For Dummies (2015)

Part III. Putting Expressions Together

Chapter 12. Initializing and Deinitializing Data

In This Chapter

arrow Understanding the reasons for initialization

arrow Initializing properties and variables

arrow Creating initializers and adding parameters

arrow Observing changes to properties

arrow Deinitializing properties and variables

In Swift, every instance of a class, structure, or enumeration must be initialized before being used. Accordingly, Swift gives you several ways to create various types of initializers to handle the process. You can also perform the initialization yourself by setting an initial or default value, which initializes the instance. In addition, many parts of the Cocoa and Cocoa Touch frameworks handle initialization for you.

For example, if you use Interface Builder to design your interface in Xcode, you can draw views and other interface elements on the Xcode canvas. At runtime, the framework instantiates the views and other interface elements that you have drawn, and in the process, it initializes them.

As another example, consider the Master-Detail Application template that is the basis of Locatapp, the example used throughout this book. Do a search for init in Locatapp and you’ll only get an error message (“Failed to initialize the application’s saved data”) from the Build Settings pane. The many initializers that you use through that template (and your app) are all hidden deep inside the Cocoa Touch framework.

It’s nice that Cocoa and Cocoa Touch can help out with initialization, but for classes that you create, you’ll have to think about creating and using initializers yourself. You also may have to think about deinitializers — functions called when an instance is about to be deallocated. This chapter introduces you to both of these parts of the initialization process.

icon tip This chapter shows you how to manage initialization in the classes that you create. Perhaps more important, it shows you how to use the initializers in classes that you use from the frameworks. There are a number of other initialization techniques that you can use in your own classes, but often you are confronted with initialization code that you must use when you use the Xcode templates or the sample code on developer.apple.com. This chapter helps you with initialization tasks you’ll need to do even if you're not creating your own classes.

Understanding Initialization

Enforcing Swift’s initialization rule means that you cannot have uninitialized instances lying around, and this, in turn, means that you cannot accidentally attempt to access an incomplete instance object. Try this in a playground:

var w: UIWindow
println (w)

You’ll get an error right away (or at least as soon as the playground finishes parsing your code). You can see the result in Figure 12-1.

image

Figure 12-1: If you use an uninitialized variable, you’ll get an error.

What if you don’t know what you want to set w to? You can get around this problem by making w an optional, as in this example (shown in Figure 12-2):

var w: UIWindow?
println (w)

image

Figure 12-2: Optionals are automatically initialized to nil unless you specify otherwise.

In this code, the variable has been automatically set to nil, and, therefore, it is initialized with that value, so you can use it without creating an error.

You may think that you can get around all of this initialization stuff just by declaring everything as an optional. It’s true that this approach keeps you from having to initialize everything — because everything is automatically set to nil — but does this really save time or effort? You can still access these initialized properties or variables, but before you do so, you’ll have to check to see whether each property or variable is nil. This means that your code is going to be littered throughout with phrases like this:

if w != nil {do something with w}

You’re going to have to include a line like that every time you use w. In the end, it’s better to initialize properties without using optionals. Initialization takes a little more typing, but it also takes serious consideration of the way you’ll be using the property or variable, which can be invaluable as you build your app (and also as you become more and more familiar with Cocoa or Cocoa Touch).

Performing Initialization

There are a number of ways to initialize stored properties. The major ones fall into two categories:

· Default values are set in a declaration, as in:

var x = 17

· Initial values are set in an initializer, as in:

x = 17

The end result is the same, but the preferred approach is to use a default value in the declaration when possible. There are two reasons why this is preferred: First, because it places the declaration and value together, it can make maintenance easier over time. Second, the use of a default value can allow Swift to infer the type of the property. You can always add the type to the declaration if you think that the type might not be inferred correctly (as for example, when the inferred type is a subclass of the class you really want to use).

Setting default values for stored properties

Classes and structures in Swift both can contain stored properties because they are stored as part of the object they belong to. Technically, they are stored as part of each instance of the class or structure. Computed properties are calculated as needed and are not stored; they are not initialized because nothing is stored. (See Chapter 16 for more.)

As with variables that aren’t part of a structure or class, stored properties can be constants (declared with let) or variables (declared with var). Their type is either declared or inferred from the initial value. If you do not provide an initial value, you must provide a declared type. Classes in most object-oriented programming languages can contain some kind of properties or other declarations. Structures can contain fields or components (or other terms) in C and its derivatives. The ability to place functions or other code in objects is common in object-oriented programming languages but placing them in structures is less so.

A stored property is declared in a class or structure as I describe in Chapter 6. Except for optional properties, every property in a class or structure must have a value when initialization is completed. Figure 12-3 shows how you can set default values using a playground and demonstrates the use of stored properties (myInstance.myProperty), as well as the use of global variables (myProperty), and the initialization of both.

image

Figure 12-3: Initializing a global variable and a stored property.

icon tip There is more on properties in Chapter 16.

The following steps show you how to produce the code in Figure 12-3:

1. Declare a global variable called myProperty (line 5) and set its default value to 6.

var myProperty :Int = 6

2. Declare a class called MyClass ( line 7 ).

class MyClass {
}

3. In the body of the class, declare a property called myProperty (line 8) and set its default value

myProperty :Int = 7

4. Create a global variable called myInstance and set it to an instance of MyClass (line 11).

var myInstance = MyClass ()

5. Print the instance myProperty (line 12).

Note that the value is 7.

6. Print the global myProperty (line 14).

Note that the value is 6.

You can use instances of classes as values for stored properties as you see in Figure 12-4. In this case, there are two classes (MyClass and MyClass2). MyClass is the same as the one shown in Figure 12-3, but it has an additional stored property — myProperty2. It is set to an instance ofMyClass2, which is created inside MyClass. Figure 12-4 shows the values of the global property (myProperty), the stored property (myInstance.myProperty), and the new stored property containing an instance of MyClass2.

image

Figure 12-4: Creating initial values for stored properties that use instances of other classes.

Creating initializers for stored properties

You can declare an initializer function (or method — choose whichever name you prefer) rather than setting a default value. The initializer will be called automatically when a class or structure is instantiated as an instance. For this to happen, the initializer must be placed inside the structure or class, and it must have the name init.

The following steps show you how to do this. (The code needed for these steps is shown in Listing 12-1.) They start from the use of a default value, as described previously, and show how you can replace that code with an initializer — or with several initializers. The example uses a class that contains a location stored as latitude and longitude values, along with an optional name for the location:

1. Create the class or structure.

2. Create stored properties and set them to the latitude and longitude of a place, as well as its optional name.

3. Create instances of the class and structure and store them in global variables myLocationClass and myLocationStructure.

Figure 12-5 shows what your playground should look like now.

4. If you remove the initial values of the stored properties, you will get a variety of errors, as shown in Figure 12-6.

Note that there is no error with name in either the class or the structure. That is because it is an optional and is set to nil if you don't provide another value.

5. Create initializers for both the class and structure. Each must have the name init, and it must add values to the stored properties latitude and longitude.

The values need not be constants: They could be the results of calculations or they could be expressions that include results of class methods. Figure 12-7 shows the use of constants. This is the initializer code for the class:

init () {
latitude = 10
longitude = 20
}

Note that the initializers are within the class and structure objects.

image

Figure 12-5: Initializing a global variable and a stored property.

image

Figure 12-6: The initial values are essential to avoid errors.

image

Figure 12-7: Using initializers to set the values of the stored properties.

Listing 12-1: Using Initializers for a Class and a Structure

// Playground - noun: a place where people can play

import UIKit

class LocationClass {
var latitude: Double
var longitude: Double
var name: String?

init () {
latitude = 10
longitude = 20
}
}

var myLocationClass = LocationClass ()

struct LocationStruct {
var latitude: Double
var longitude: Double
var name: String? = nil

init () {
latitude = 30
longitude = 40
}
}
var myLocationStruct = LocationStruct ()

Adding parameters to initializers

Listing 12-1 and Figure 12-7 show the simplest initializer syntax. You can make more complex initializers by adding parameters. When you add parameters to an initializer, you create a specific initializer that uses those parameters — even though its name is init.

Setting initial values in an initializer

For example, instead of using constants like 10, 20, 30, 40, and so forth, you could actually pass in the values for latitude and longitude. This means that the code to create and initialize LocationClass would look like this:

var myLocation = LocationClass
(latitude: 10, longitude: 20)

The initializer inside the class declaration would look like this:

init (latitude: Double, longitude: Double) {
self.latitude = latitude
self.longitude = longitude
}

Here are two points to notice:

· Handle ambiguous names: Because you have parameters to init named latitude and longitude, there is ambiguity if you leave the names of the properties in the function as latitude and longitude. In this case, make it clear that you are setting the class properties to the values of the init parameters. Change the code to reference self (the class itself), as in the following:

self.latitude = latitude
self.longitude = longitude

· Optionals don't need to be set: You can always come back and set name because it is an optional (notice the question mark in String?). Some people prefer to initialize every stored property including those that don't necessarily need it (the optionals). Other people prefer to initialize every necessary stored property including those that could be automatically set to nil but that you want to set to a value that you consider more usable.

Using external names in an initializer

Each of the arguments to init can have an external name that may or may not be the name of the property. Using the previous example with external names, the code could look like this:

var myLocationClass = LocationClass (
digitalLatitude longitude: 15,
digtitalLongitude longitude: 25,
name: "The Place")

The initializer that uses external names would now look like this:

init (digitalLatitude latitude: Double,
digitalLongitude longitude: Double,
name: String?) {

self.latitude = latitude
self.longitude = longitude
self.name= name
}

For the declaration of init, you provide the external name, the internal name, and the type for each parameter. When you call the initializer, you provide the external name and the value for each parameter. Note that in this case, the external names are longer and provide more information (the fact that these are digital and not degree-minute-second values). Internally, the simpler name is used.

If no external name is provided, you can just use the internal name when you call the function.

This is an area of Swift that has changed over time.

Using an expression in an initializer

You don’t have to pass values for properties into the init. Instead, you can pass in a separate value that is used to calculate one or more properties. Here is an example of an init that accepts an address string and geocodes (through the magic of a comment) it into latitude and longitude values:

init (address: NSString) {
// do geocoding to set self.latitude and
// self.longitude
}

Using multiple initializers

All initializers are named init, but they may vary based on their parameters. Figure 12-8 and Listing 12-2 show two initializers in the example code that has been used in this chapter. One sets the stored properties based on values passed in, and the other uses the expression shown in the previous section. You can write both initializers (or even more) and then use which one you want.

image

Figure 12-8: Using multiple initializers.

Listing 12-2: Using Multiple Initializers

class LocationClass {
var latitude: Double
var longitude: Double
var name: String?

init (digitalLatitude latitude: Double,
digitalLongitude longitude: Double,
name: String?) {
latitude = latitude
longitude = longitude
self.name = name
}

init (address: NSString) {
// convert address to self.latitude and self.longitude
}
}

Understanding Deinitialization

A deinitializer is named deinit. It has no parameters. You can use it to clean up a class when it is deallocated. (Although intializers are available to classes and structures, deinitializers are available only to classes.)

Deinitialization may mean moving values from properties that are about to disappear to other properties that will remain (such as in a Core Data persistent store). You don’t have to worry about releasing memory: Swift uses Automatic Reference Counting (ARC) to get rid of unneeded objects and free up memory. However, although ARC handles the object memory issues, it doesn’t handle data persistence, which is one use for a deinitializer.

Deinitializers are all called deinit; they take no parameters. Here’s the basic structure:

deinit {
// store last location in a persistent store
}

You could use this to keep track of the last location used.