Initialization
- Initialization is the process of preparing an instance of a class, structure, or enumeration for use.
- Unlike Objective-C initializers, Swift initializers do not return a value. Their primary role is to ensure that new instances of a type are correctly initialized before they are used for the first time.
Setting Initial Values for Stored Properties
- Classes and structures must set all of their stored properties
- When you assign a default value to a stored property, or set its initial value within an initializer, the value of that property is set directly, without calling any property observers.
- set an initial value for a stored property within an initializer, or by assigning a default property value as part of the property’s definition.
Initializers
1 | init() { |
Default Property Values
1 | struct Fahrenheit { |
Customizing Initialization
Initialization Parameters
1 | struct Celsius { |
Parameter Names and Argument Labels
Swift provides an automatic argument label for every parameter in an initializer if you don’t provide one.
1 | struct Color { |
Initializer Parameters Without Argument Labels
1 | struct Celsius { |
Optional Property Types
- stored property that is logically allowed to have “no value”
- Properties of optional type are automatically initialized with a value of nil, indicating that the property is deliberately intended to have “no value yet” during initialization.
1 | class SurveyQuestion { |
Assigning Constant Properties During Initialization
For class instances, a constant property can be modified during initialization only by the class that introduces it. It cannot be modified by a subclass.
1 | class SurveyQuestion { |
Default Initializers
- Swift provides a default initializer for any structure or class that provides default values for all of its properties and does not provide at least one initializer itself.
- The default initializer simply creates a new instance with all of its properties set to their default values.
1 | class ShoppingListItem { |
Memberwise Initializers for Structure Types
- Structure types automatically receive a memberwise initializer if they do not define any of their own custom initializers.
- Unlike a default initializer, the structure receives a memberwise initializer even if it has stored properties that do not have default values.
1 | struct Size { |
Initializer Delegation for Value Types
- Initializers can call other initializers to perform part of an instance’s initialization. This process, known as initializer delegation
- Value types: initializer delegation is simple,they can only delegate to another initializer that they provide themselves.
- Classes: ensuring that all stored properties they inherit are assigned a suitable value during initialization.
- if you define a custom initializer for a value type, you will no longer have access to the default initializer .If you want your custom value type to be initializable with the default initializer and memberwise initializer, and also with your own custom initializers, write your custom initializers in an extension rather than as part of the value type’s original implementation.
1 | struct Size { |
Class Inheritance and Initialization
- All of a class’s stored properties—including any properties the class inherits from its superclass—must be assigned an initial value during initialization.
- Swift defines two kinds of initializers for class types to help ensure all stored properties receive an initial value. These are known as designated initializers and convenience initializers.
Designated Initializers and Convenience Initializers
Designated initialzer
- Designated initializers are the primary initializers for a class. A designated initializer fully initializes all properties introduced by that class and calls an appropriate superclass initializer to continue the initialization process up the superclass chain.
- Must have one, normally one ,sometimes inherits one or more from superclass
Convenience initializer
- Convenience initializers are secondary, supporting initializers for a class.
- You do not have to provide convenience initializers if your class does not require them.
Syntax for Designated and Convenience Initializers
1 | // Designated Initializers |
Initializer Delegation for Class Types
Rule 1
A designated initializer must call a designated initializer from its immediate superclass.
Rule 2
A convenience initializer must call another initializer from the same class.
Rule 3
A convenience initializer must ultimately call a designated initializer.
A simple way to remember this is:
- Designated initializers must always delegate up.
- Convenience initializers must always delegate across.
Simple Example
Complicated example
Two-Phase Initialization
Two phase
Class initialization in Swift is a two-phase process.
- In the first phase, each stored property is assigned an initial value by the class that introduced it.
- each class is given the opportunity to customize its stored properties further
Swfit & Objective-C
Swift’s two-phase initialization process is similar to initialization in Objective-C. The main difference is that during phase .Objective-C assigns zero or null values (such as 0 or nil) to every property. Swift’s initialization flow is more flexible in that it lets you set custom initial values, and can cope with types for which 0 or nil is not a valid default value.
Safety check
Swift’s compiler performs four helpful safety-checks to make sure that two-phase initialization is completed without error:
- A designated initializer must ensure that all of the properties introduced by its class are initialized before it delegates up to a superclass initializer.(Stored property first then superclass)
- A designated initializer must delegate up to a superclass initializer before assigning a value to an inherited property. If it doesn’t, the new value the designated initializer assigns will be overwritten by the superclass as part of its own initialization.
- A convenience initializer must delegate to another initializer before assigning a value to any property (including properties defined by the same class). If it doesn’t, the new value the convenience initializer assigns will be overwritten by its own class’s designated initializer.
- An initializer cannot call any instance methods, read the values of any instance properties, or refer to self as a value until after the first phase of initialization is complete.
Phase 1
- A designated or convenience initializer is called on a class.
- Memory for a new instance of that class is allocated. The memory is not yet initialized.
- A designated initializer for that class confirms that all stored properties introduced by that class have a value. The memory for these stored properties is now initialized.
- The designated initializer hands off to a superclass initializer to perform the same task for its own stored properties.
- This continues up the class inheritance chain until the top of the chain is reached.
- Once the top of the chain is reached, and the final class in the chain has ensured that all of its stored properties have a value, the instance’s memory is considered to be fully initialized, and phase 1 is complete.
Phase 2
- Working back down from the top of the chain, each designated initializer in the chain has the option to customize the instance further. Initializers are now able to access self and can modify its properties, call its instance methods, and so on.
- Finally, any convenience initializers in the chain have the option to customize the instance and to work with self.
Initializer Inheritance and Overriding
- Unlike subclasses in Objective-C, Swift subclasses do not inherit their superclass initializers by default.
- If you want a custom subclass to present one or more of the same initializers as its superclass, you can provide a custom implementation of those initializers within the subclass.
- You always write the override modifier when overriding a superclass designated initializer, even if your subclass’s implementation of the initializer is a convenience initializer.
- you do not write the override modifier when providing a matching implementation of a superclass convenience initializer
- Subclasses can modify inherited variable properties during initialization, but can not modify inherited constant properties.
1 | class Vehicle { |
Automatic Initializer Inheritance
Rule 1
If your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializers.
Rule 2
If your subclass provides an implementation of all of its superclass designated initializers—either by inheriting them as per rule 1, or by providing a custom implementation as part of its definition—then it automatically inherits all of the superclass convenience initializers.
A subclass can implement a superclass designated initializer as a subclass convenience initializer as part of satisfying rule 2.
Designated and Convenience Initializers in Action
- Classes do not have a default memberwise initializer
Basic class
1 | class Food { |
Second hierachy class
RecipeIngredient has nonetheless provided an implementation of all of its superclass’s designated initializers. Therefore, RecipeIngredient automatically inherits all of its superclass’s convenience initializers too.
1 | class RecipeIngredient: Food { |
Third hierachy class
Because it provides a default value for all of the properties it introduces and does not define any initializers itself, ShoppingListItem automatically inherits all of the designated and convenience initializers from its superclass.
1 | class ShoppingListItem: RecipeIngredient { |
Failable Initializers
- You write a failable initializer by placing a question mark after the init keyword (init?).
- You cannot define a failable and a nonfailable initializer with the same parameter types and names.
- A failable initializer creates an optional value of the type it initializes. You write return nil within a failable initializer to indicate a point at which initialization failure can be triggered.
- Strictly speaking, initializers do not return a value. Rather, their role is to ensure that self is fully and correctly initialized by the time that initialization ends. Although you write return nil to trigger an initialization failure, you do not use the return keyword to indicate initialization success.
- Checking for an empty string value (such as “” rather than “Giraffe”) is not the same as checking for nil to indicate the absence of an optional String value. In the example below, an empty string (“”) is a valid, nonoptional String.
1 | struct Animal { |
Failable Initializers for Enumerations
1 | enum TemperatureUnit { |
Failable Initializers for Enumerations with Raw Values
- Enumerations with raw values automatically receive a failable initializer,
init?(rawValue:)
1 | enum TemperatureUnit: Character { |
Propagation of Initialization Failure
- A failable initializer of a class, structure, or enumeration can delegate across to another failable initializer from the same class, structure, or enumeration. Similarly, a subclass failable initializer can delegate up to a superclass failable initializer.
- A failable initializer can also delegate to a nonfailable initializer. Use this approach if you need to add a potential failure state to an existing initialization process that does not otherwise fail.
1 | class Product { |
Overriding a Failable Initializer
- You can override a superclass failable initializer in a subclass, just like any other initializer
- Alternatively, you can override a superclass failable initializer with a subclass nonfailable initializer. the only way to delegate up to the superclass initializer is to force-unwrap the result of the failable superclass initializer.
- You can override a failable initializer with a nonfailable initializer but not the other way around.
1 | class Document { |
The init! Failable Initializer
- You typically define a failable initializer that creates an optional instance of the appropriate type by placing a question mark after the init keyword (init?).
- define a failable initializer that creates an implicitly unwrapped optional instance of the appropriate type. Do this by placing an exclamation mark after the init keyword (init!) instead of a question mark.
- You can delegate from init? to init! and vice versa,
- you can override init? with init! and vice versa.
- You can also delegate from init to init!, although doing so will trigger an assertion if the init! initializer causes initialization to fail.
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: - You do not write the
override
modifier when overriding a required designated initializer:
1 | class SomeClass { |
Setting a Default Property Value with a Closure or Function
- If you omit these parentheses, you are trying to assign the closure itself to the property, and not the return value of the closure.
- If you use a closure to initialize a property, remember that the rest of the instance has not yet been initialized at the point that the closure is executed. This means that you cannot access any other property values from within your closure, even if those properties have default values. You also cannot use the implicit self property, or call any of the instance’s methods.
1 | class SomeClass { |
game board example
1 |
|
Deinitialization
- A deinitializer is called immediately before a class instance is deallocated.
- Deinitializers are only available on class types.
How Deinitialization Works
- Swift automatically deallocates your instances when they are no longer needed.(ARC)
- However, when you are working with your own resources, you might need to perform some additional cleanup yourself. 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
- Deinitializers are called automatically, just before instance deallocation takes place. You are not allowed to call a deinitializer yourself.
- 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 if a subclass does not provide its own deinitializer.
- Class definitions can have at most one deinitializer per class. The deinitializer does not take any parameters and is written without parentheses:
1 | deinit { |
Deinitializers in Action
1 | class Bank { |