Swift - Initialization


Advertisements

Classes, structures and enumerations once declared in Swift 4 are initialized for preparing instance of a class. Initial value is initialized for stored property and also for new instances too the values are initialized to proceed further. The keyword to create initialization function is carried out by 'init()' method. Swift 4 initializer differs from Objective-C that it does not return any values. Its function is to check for initialization of newly created instances before its processing. Swift 4 also provides 'deinitialization' process for performing memory management operations once the instances are deallocated.

Initializer Role for Stored Properties

Stored property have to initialize the instances for its classes and structures before processing the instances. Stored properties use initializer to assign and initialize values thereby eradicating the need to call property observers. Initializer is used in stored property

  • To create an initial value.

  • To assign default property value within the property definition.

  • To initialize an instance for a particular data type 'init()' is used. No arguments are passed inside the init() function.

Syntax

init() {
   //New Instance initialization goes here
}

Example

struct rectangle {
   var length: Double
   var breadth: Double
   init() {
      length = 6
      breadth = 12
   }
}

var area = rectangle()
print("area of rectangle is \(area.length*area.breadth)")

When we run the above program using playground, we get the following result −

area of rectangle is 72.0

Here the structure 'rectangle' is initialized with members length and breadth as 'Double' datatypes. Init() method is used to initialize the values for the newly created members length and double. Area of rectangle is calculated and returned by calling the rectangle function.

Setting Property Values by Default

Swift 4 language provides Init() function to initialize the stored property values. Also, the user has provision to initialize the property values by default while declaring the class or structure members. When the property takes the same value alone throughout the program we can declare it in the declaration section alone rather than initializing it in init(). Setting property values by default enables the user when inheritance is defined for classes or structures.

struct rectangle {
   var length = 6
   var breadth = 12
}

var area = rectangle()
print("area of rectangle is \(area.length*area.breadth)")

When we run the above program using playground, we get the following result −

area of rectangle is 72

Here instead of declaring length and breadth in init() the values are initialized in declaration itself.

Parameters Initialization

In Swift 4 language the user has the provision to initialize parameters as part of the initializer's definition using init().

struct Rectangle {
   var length: Double
   var breadth: Double
   var area: Double
   
   init(fromLength length: Double, fromBreadth breadth: Double) {
      self.length = length
      self.breadth = breadth
      area = length * breadth
   }
   init(fromLeng leng: Double, fromBread bread: Double) {
      self.length = leng
      self.breadth = bread
      area = leng * bread
   }
}

let ar = Rectangle(fromLength: 6, fromBreadth: 12)
print("area is: \(ar.area)")

let are = Rectangle(fromLeng: 36, fromBread: 12)
print("area is: \(are.area)")

When we run the above program using playground, we get the following result −

area is: 72.0
area is: 432.0

Local & External Parameters

Initialization parameters have both local and global parameter names similar to that of function and method parameters. Local parameter declaration is used to access within the initialize body and external parameter declaration is used to call the initializer. Swift 4 initializers differ from function and method initializer that they do not identify which initializer are used to call which functions.

To overcome this, Swift 4 introduces an automatic external name for each and every parameter in init(). This automatic external name is as equivalent as local name written before every initialization parameter.

struct Days {
   let sunday, monday, tuesday: Int
   init(sunday: Int, monday: Int, tuesday: Int) {
      self.sunday = sunday
      self.monday = monday
      self.tuesday = tuesday
   }
   init(daysofaweek: Int) {
      sunday = daysofaweek
      monday = daysofaweek
      tuesday = daysofaweek
   }
}

let week = Days(sunday: 1, monday: 2, tuesday: 3)
print("Days of a Week is: \(week.sunday)")
print("Days of a Week is: \(week.monday)")
print("Days of a Week is: \(week.tuesday)")

let weekdays = Days(daysofaweek: 4)
print("Days of a Week is: \(weekdays.sunday)")
print("Days of a Week is: \(weekdays.monday)")
print("Days of a Week is: \(weekdays.tuesday)")

When we run the above program using playground, we get the following result −

Days of a Week is: 1
Days of a Week is: 2
Days of a Week is: 3
Days of a Week is: 4
Days of a Week is: 4
Days of a Week is: 4

Parameters without External Names

When an external name is not needed for an initialize underscore '_' is used to override the default behavior.

struct Rectangle {
   var length: Double
   
   init(frombreadth breadth: Double) {
      length = breadth * 10
   }
   init(frombre bre: Double) {
      length = bre * 30
   }
   init(_ area: Double) {
      length = area
   }
}

let rectarea = Rectangle(180.0)
print("area is: \(rectarea.length)")

let rearea = Rectangle(370.0)
print("area is: \(rearea.length)")

let recarea = Rectangle(110.0)
print("area is: \(recarea.length)")

When we run the above program using playground, we get the following result −

area is: 180.0
area is: 370.0
area is: 110.0

Optional Property Types

When the stored property at some instance does not return any value that property is declared with an 'optional' type indicating that 'no value' is returned for that particular type. When the stored property is declared as 'optional' it automatically initializes the value to be 'nil' during initialization itself.

struct Rectangle {
   var length: Double?
   
   init(frombreadth breadth: Double) {
      length = breadth * 10
   }
   init(frombre bre: Double) {
      length = bre * 30
   }
   init(_ area: Double) {
      length = area
   }
}

let rectarea = Rectangle(180.0)
print("area is: \(rectarea.length)")

let rearea = Rectangle(370.0)
print("area is: \(rearea.length)")

let recarea = Rectangle(110.0)
print("area is: \(recarea.length)")

When we run the above program using playground, we get the following result −

area is: Optional(180.0)
area is: Optional(370.0)
area is: Optional(110.0)

Modifying Constant Properties During Initialization

Initialization also allows the user to modify the value of constant property too. During initialization, class property allows its class instances to be modified by the super class and not by the subclass. Consider for example in the previous program 'length' is declared as 'variable' in the main class. The below program variable 'length' is modified as 'constant' variable.

struct Rectangle {
   let length: Double?
   
   init(frombreadth breadth: Double) {
      length = breadth * 10
   }
   init(frombre bre: Double) {
      length = bre * 30
   }
   init(_ area: Double) {
      length = area
   }
}

let rectarea = Rectangle(180.0)
print("area is: \(rectarea.length)")

let rearea = Rectangle(370.0)
print("area is: \(rearea.length)")

let recarea = Rectangle(110.0)
print("area is: \(recarea.length)")

When we run the above program using playground, we get the following result −

area is: Optional(180.0)
area is: Optional(370.0)
area is: Optional(110.0)

Default Initializers

Default initializers provide a new instance to all its declared properties of base class or structure with default values.

class defaultexample {
   var studname: String?
   var stmark = 98
   var pass = true
}
var result = defaultexample()

print("result is: \(result.studname)")
print("result is: \(result.stmark)")
print("result is: \(result.pass)")

When we run above program using playground, we get following result. −

result is: nil
result is: 98
result is: true

The above program is defined with class name as 'defaultexample'. Three member functions are initialized by default as 'studname?' to store 'nil' values, 'stmark' as 98 and 'pass' as Boolean value 'true'. Likewise the member values in the class can be initialized as default before processing the class member types.

Memberwise Initializers for Structure Types

When the custom initializers are not provided by the user, Structure types in Swift 4 will automatically receive the 'memberwise initializer'. Its main function is to initialize the new structure instances with the default memberwise initialize and then the new instance properties are passed to the memberwise initialize by name.

struct Rectangle {
   var length = 100.0, breadth = 200.0
}
let area = Rectangle(length: 24.0, breadth: 32.0)

print("Area of rectangle is: \(area.length)")
print("Area of rectangle is: \(area.breadth)")

When we run the above program using playground, we get the following result −

Area of rectangle is: 24.0
Area of rectangle is: 32.0

Structures are initialized by default for their membership functions during initialization for 'length' as '100.0' and 'breadth' as '200.0'. But the values are overridden during the processing of variables length and breadth as 24.0 and 32.0.

Initializer Delegation for Value Types

Initializer Delegation is defined as calling initializers from other initializers. Its main function is to act as reusability to avoid code duplication across multiple initializers.

struct Stmark {
   var mark1 = 0.0, mark2 = 0.0
}
struct stdb {
   var m1 = 0.0, m2 = 0.0
}

struct block {
   var average = stdb()
   var result = Stmark()
   init() {}
   init(average: stdb, result: Stmark) {
      self.average = average
      self.result = result
   }

   init(avg: stdb, result: Stmark) {
      let tot = avg.m1 - (result.mark1 / 2)
      let tot1 = avg.m2 - (result.mark2 / 2)
      self.init(average: stdb(m1: tot, m2: tot1), result: result)
   }
}

let set1 = block()
print("student result is: \(set1.average.m1, set1.average.m2)
\(set1.result.mark1, set1.result.mark2)")

let set2 = block(average: stdb(m1: 2.0, m2: 2.0),
result: Stmark(mark1: 5.0, mark2: 5.0))
print("student result is: \(set2.average.m1, set2.average.m2)
\(set2.result.mark1, set2.result.mark2)")

let set3 = block(avg: stdb(m1: 4.0, m2: 4.0),
result: Stmark(mark1: 3.0, mark2: 3.0))
print("student result is: \(set3.average.m1, set3.average.m2)
\(set3.result.mark1, set3.result.mark2)")

When we run the above program using playground, we get the following result −

(0.0,0.0) (0.0,0.0)
(2.0,2.0) 5.0,5.0)
(2.5,2.5) (3.0,3.0)

Rules for Initializer Delegation

Value Types Class Types
Inheritance is not supported for value types like structures and enumerations. Referring other initializers is done through self.init Inheritance is supported. Checks all stored property values are initialized

Class Inheritance and Initialization

Class types have two kinds of initializers to check whether defined stored properties receive an initial value namely designated initializers and convenience initializers.

Designated Initializers and Convenience Initializers

Designated Initializer Convenience Initializer
Considered as primary initializes for a class Considered as supporting initialize for a class
All class properties are initialized and appropriate superclass initializer are called for further initialization Designated initializer is called with convenience initializer to create class instance for a specific use case or input value type
At least one designated initializer is defined for every class No need to have convenience initializers compulsory defined when the class does not require initializers.
Init(parameters) { statements } convenience init(parameters) { statements }

Program for Designated Initializers

class mainClass {
   var no1 : Int // local storage
   init(no1 : Int) {
      self.no1 = no1 // initialization
   }
}

class subClass : mainClass {
   var no2 : Int // new subclass storage
   init(no1 : Int, no2 : Int) {
      self.no2 = no2 // initialization
      super.init(no1:no1) // redirect to superclass
   }
}

let res = mainClass(no1: 10)
let print = subClass(no1: 10, no2: 20)

print("res is: \(res.no1)")
print("res is: \(print.no1)")
print("res is: \(print.no2)")

When we run the above program using playground, we get the following result −

res is: 10
res is: 10
res is: 20

Program for Convenience Initializers

class mainClass {
   var no1 : Int // local storage
   init(no1 : Int) {
      self.no1 = no1 // initialization
   }
}

class subClass : mainClass {
   var no2 : Int
   init(no1 : Int, no2 : Int) {
      self.no2 = no2
      super.init(no1:no1)
   }
   // Requires only one parameter for convenient method
   override convenience init(no1: Int) {
      self.init(no1:no1, no2:0)
   }
}

let res = mainClass(no1: 20)
let print = subClass(no1: 30, no2: 50)

print("res is: \(res.no1)")
print("res is: \(print.no1)")
print("res is: \(print.no2)")

When we run the above program using playground, we get the following result −

res is: 20
res is: 30
res is: 50

Initializer Inheritance and Overriding

Swift 4 does not allow its subclasses to inherit its superclass initializers for their member types by default. Inheritance is applicable to Super class initializers only to some extent which will be discussed in Automatic Initializer Inheritance.

When the user needs to have initializers defined in super class, subclass with initializers has to be defined by the user as custom implementation. When overriding has to be taken place by the sub class to the super class 'override' keyword has to be declared.

class sides {
   var corners = 4
   var description: String {
      return "\(corners) sides"
   }
}

let rectangle = sides()
print("Rectangle: \(rectangle.description)")

class pentagon: sides {
   override init() {
      super.init()
      corners = 5
   }
}

let bicycle = pentagon()
print("Pentagon: \(bicycle.description)")

When we run the above program using playground, we get the following result −

Rectangle: 4 sides
Pentagon: 5 sides

Designated and Convenience Initializers in Action

class Planet {
   var name: String
   init(name: String) {
      self.name = name
   }
   convenience init() {
      self.init(name: "[No Planets]")
   }
}

let plName = Planet(name: "Mercury")
print("Planet name is: \(plName.name)")

let noplName = Planet()
print("No Planets like that: \(noplName.name)")

class planets: Planet {
   var count: Int
   init(name: String, count: Int) {
      self.count = count
      super.init(name: name)
   }
   override convenience init(name: String) {
      self.init(name: name, count: 1)
   }
}

When we run the above program using playground, we get the following result −

Planet name is: Mercury
No Planets like that: [No Planets]

Failable Initializer

The user has to be notified when there are any initializer failures while defining a class, structure or enumeration values. Initialization of variables sometimes become a failure one due to−

  • Invalid parameter values.
  • Absence of required external source.
  • Condition preventing initialization from succeeding.

To catch exceptions thrown by initialization method, Swift 4 produces a flexible initialize called 'failable initializer' to notify the user that something is left unnoticed while initializing the structure, class or enumeration members. Keyword to catch the failable initializer is 'init?'. Also, failable and non failable initializers cannot be defined with same parameter types and names.

struct studrecord {
   let stname: String
   init?(stname: String) {
      if stname.isEmpty {return nil }
      self.stname = stname
   }
}
let stmark = studrecord(stname: "Swing")

if let name = stmark {
   print("Student name is specified")
}
let blankname = studrecord(stname: "")

if blankname == nil {
   print("Student name is left blank")
}

When we run the above program using playground, we get the following result −

Student name is specified
Student name is left blank

Failable Initializers for Enumerations

Swift 4 language provides the flexibility to have Failable initializers for enumerations too to notify the user when the enumeration members are left from initializing values.

enum functions {
   case a, b, c, d
   init?(funct: String) {
      switch funct {
      case "one":
         self = .a
      case "two":
         self = .b
      case "three":
         self = .c
      case "four":
         self = .d
      default:
         return nil
      }
   }
}
let result = functions(funct: "two")

if result != nil {
   print("With In Block Two")
}
let badresult = functions(funct: "five")

if badresult == nil {
   print("Block Does Not Exist")
}

When we run the above program using playground, we get the following result −

With In Block Two
Block Does Not Exist

Failable Initializers for Classes

A failable initializer when declared with enumerations and structures alerts an initialization failure at any circumstance within its implementation. However, failable initializer in classes will alert the failure only after the stored properties have been set to an initial value.

class studrecord {
   let studname: String!
   init?(studname: String) {
      self.studname = studname
      if studname.isEmpty { return nil }
   }
}

if let stname = studrecord(studname: "Failable Initializers") {
   print("Module is \(stname.studname)")
}

When we run the above program using playground, we get the following result −

Module is Optional("Failable Initializers")

Overriding a Failable Initializer

Like that of initialize the user also has the provision to override a superclass failable initializer inside the sub class. Super class failable initialize can also be overridden with in a sub class non-failable initializer.

Subclass initializer cannot delegate up to the superclass initializer when overriding a failable superclass initializer with a non-failable subclass initialize.

A non-failable initializer can never delegate to a failable initializer.

The program given below describes the failable and non-failable initializers.

class Planet {
   var name: String
   
   init(name: String) {
      self.name = name
   }
   convenience init() {
      self.init(name: "[No Planets]")
   }
}
let plName = Planet(name: "Mercury")
print("Planet name is: \(plName.name)")

let noplName = Planet()
print("No Planets like that: \(noplName.name)")
   
class planets: Planet {
   var count: Int
   
   init(name: String, count: Int) {
      self.count = count
      super.init(name: name)
   }
   override convenience init(name: String) {
      self.init(name: name, count: 1)
   }
}

When we run the above program using playground, we get the following result −

Planet name is: Mercury
No Planets like that: [No Planets]

The init! Failable Initializer

Swift 4 provides 'init?' to define an optional instance failable initializer. To define an implicitly unwrapped optional instance of the specific type 'init!' is specified.

struct studrecord {
let stname: String

   init!(stname: String) {
      if stname.isEmpty {return nil }
      self.stname = stname
   }
}
let stmark = studrecord(stname: "Swing")

if let name = stmark {
   print("Student name is specified")
}

let blankname = studrecord(stname: "")

if blankname == nil {
   print("Student name is left blank")
}

When we run the above program using playground, we get the following result −

Student name is specified
Student name is left blank

Required Initializers

To declare each and every subclass of the initialize 'required' keyword needs to be defined before the init() function.

class classA {
   required init() {
      var a = 10
      print(a)
   }
}

class classB: classA {
   required init() {
      var b = 30
      print(b)
   }
}

let res = classA()
let print = classB()

When we run the above program using playground, we get the following result −

10
30
10
Advertisements