class init - blky/IOS-Swift GitHub Wiki
Swift provides a default initializer for any structure or base class that provides default values for all of its properties and does not provide at least one initializer itself.
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.
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.
- Classes tend to have very few designated initializers, and it is quite common for a class to have only one. Every class must have at least one designated initializer.
- In some cases, this requirement is satisfied by inheriting one or more designated initializers from a superclass,
- are secondary, supporting initializers for a class.
- You can define a convenience initializer to call a designated initializer from the same class as the convenience initializer with some of the designated initializer’s parameters set to default values.
class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
}
}
- You can also define a convenience initializer to create an instance of that class for a specific use case or input value type.
convenience init(parameters) {
statements
}
- A simple way to remember this is:
Designated initializers must always delegate up.
Convenience initializers must always delegate across.
In the first phase, each stored property is assigned an initial value by the class that introduced it. Once the initial state for every stored property has been determined, the second phase begins, and each class is given the opportunity to customize its stored properties further before the new instance is considered ready for use.
-
is similar to initialization in Objective-C.
-
The main difference is that during phase 1, 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.
- Properties can only be accessed, and methods can only be called, once the class instance is known to be valid at the end of the first phase.
- if you write a subclass initializer that matches a superclass convenience initializer, that superclass convenience initializer can never be called directly by your 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.
-
Subclasses can modify inherited variable properties during initialization, but can not modify inherited constant properties
- It is sometimes useful to define a class, structure, or enumeration for which initialization can fail.
- You write a failable initializer by placing a question mark after the init keyword (init?).
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}
let anonymousCreature = Animal(species: "")
// anonymousCreature is of type Animal?, not Animal
if anonymousCreature == nil {
println("The anonymous creature could not be initialized")
}
// prints "The anonymous creature could not be initialized"
}
another example
class CartItem: Product {
let quantity: Int!
init?(name: String, quantity: Int) {
super.init(name: name)
if quantity < 1 { return nil }
self.quantity = quantity
}
}
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 non-failable initializer. This enables you to define a subclass for which initialization cannot fail, even though initialization of the superclass is allowed to fail.
- You can override a failable initializer with a nonfailable initializer but not the other way around.
The example below defines a class called Document. This class models a document that can be be initialized with a name property that is either a non-empty string value or nil, but cannot be an empty string:
class Document {
var name: String?
// this initializer creates a document with a nil name value
init() {}
// this initializer creates a document with a non-empty name value
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
The next example defines a subclass of Document called AutomaticallyNamedDocument. The AutomaticallyNamedDocument subclass overrides both of the designated initializers introduced by Document. These overrides ensure that an AutomaticallyNamedDocument instance has an initial name value of "[Untitled]" if the instance is initialized without a name, or if an empty string is passed to the init(name:) initializer:
class AutomaticallyNamedDocument: Document {
override init() {
super.init()
self.name = "[Untitled]"
}
override init(name: String) {
super.init()
if name.isEmpty {
self.name = "[Untitled]"
} else {
self.name = name
}
}
}
enum TemperatureUnit {
case Kelvin, Celsius, Fahrenheit
init?(symbol: Character) {
switch symbol {
case "K":
self = .Kelvin
case "C":
self = .Celsius
case "F":
self = .Fahrenheit
default:
return nil
}
}
an implicitly unwrapped integer type (Int!). this means that the property has a default value of nil before it is assigned a specific value during initialization.
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
}
}