Treehouse Intermediate Course Part 4 (Initialization)

I deliberately had a break yesterday - I figure it's health to 'let the information settle' and also not set an unrealistic precedent of coding every day! If we're aiming for the three hours per week, then that can start from NOW and we'll assume I hit that target last week! So continuing with initialization. Let's go!

Designated and Convenience Initializers

So Pasan makes the point that in value types (e.g. structs/enums) initialzation is only needed when doing a custom one. For reference, it is necessary and is important to refer to the super/base class...

Classes have two types of initializers.

Designated - central point of init, classes must have AT LEAST one of these, they're responsible for initializing stored properties, and responsible for calling the super init.

So so far we have been using designated ones. Classes typically have one - they have to have at least one. We'd get an error without using an init method.

class Vehicle {
    
    let name: String
    
    init(name: String) {
        
        self.name = name
    }

}

A very simple example! Got that so far. 

Convenience - these are secondary/supportive inits for a class. They can call a designated init within the same class set to a default value. It can only call a designated init called within the SAME CLASS. 

convenience init() {
        self.init(name: "Unnamed")
    }
We don't need any parameters here as we are passing through a DEFAULT value. 

Right that makes sense. In the past, I was putting in ALL of the variables/constants as parameters, when they are not needed for default values! Makes sense. 

Quiz - got 5 out of 6. 

Initializing Superclasses

class Car: Vehicle {
    
    let numberOfDoors: Int
    
    
}

So an error comes up here as there is no init....

In the subclass, we cannot call any of the convenience inits from the superclass (the vehicle class). 

   init(name: String, numberOfDoors: Int) {
        self.numberOfDoors = numberOfDoors
        super.init(name: name)
    }
}

So the designated init is done! 

convenience override init(name: String) {
        self.init(name: name, numberOfDoors: 4)
    }

I tried this on my own but made the mistake of putting in the override keyword. Wasn't needed as I wasn't using any parameters!

convenience init() {
         self.init(name: "Unnamed")
    }

What about number of doors? I suppose that there is no value for this property.

So a key rule is that convenience inits must call designated ones eventually. OK that all makes sense. 

../_images/initializerDelegation01_2x.png

Required Initializers 

Last one before the quiz for this course!

The keyword 'required' means that all subclasses must provide init for that particular method....

class SomeClass {
    
    required init() {
        
    }
}

class NewClass: SomeClass {
    
    init(test: String) {}
    
    required init() {
        fatalError()
    }
    
}

This is a better example! So to access the UIViewControllerClass, there is a certain required init. The below causes an error. 

class ViewController: UIViewController {
    init() {}
    
}


Clicking on the error reveals this (I've then delete the init line):

class ViewController: UIViewController {
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
}

And that was from the 'fix' in the error that came up. There is a NSCoder protocol that allows an archived View Controller to be unarchived. OK.....

There is no way around this as the required init is REQUIRED! No arguments can be accepted. All I need to take away from this lesson apparently is to use the required keyword for all subclasses when it has been used in the superclass. That makes sense. 

OK a bit more from the Apple Documentation....


Write the required modifier before the definition of a class initializer to indicate that every subclass of the class must implement that initializer:
  1. class SomeClass {
  2. required init() {
  3. // initializer implementation goes here
  4. }
  5. }
You must also write the required modifier before every subclass implementation of a required initializer, to indicate that the initializer requirement applies to further subclasses in the chain. You do not write the override modifier when overriding a required designated initializer:
  1. class SomeSubclass: SomeClass {
  2. required init() {
  3. // subclass implementation of the required initializer goes here
  4. }
  5. }
NOTE
You do not have to provide an explicit implementation of a required initializer if you can satisfy the requirement with an inherited initializer.
All that made sense up until the 'note'. Right, this note is saying that if you meet all of the requirements you don't need to use the 'required' keyword? Not sure. 

Quiz - 5 out of 5! Whoo!

So I've completed 'Object Initialization'. I feel much more au fait with the different keywords and what they all mean. I haven't felt the need this time to go into a big research into what the different elements meant. Perhaps because my understanding was better, or the last couple of videos were clearer - or both! Still, let's just check Ray's eBook for some general information about init....

class Student {
    let firstName: String
    let lastName: String
    var grades: [Grade] = []
    
    required init(firstName: String, lastName: String) {
        self.firstName = firstName
        self.lastName = lastName
    }
}

In the modified version of Student above, the first and last name-based initializer has been marked with the keyword required. This keyword will force all subclasses of Student to implement this initializer.
Now that there’s a required initializer on Student, StudentAthlete must override and implement it too.


lass StudentAthlete: Student {
    // Now required by the compiler!
    required init(firstName: String, lastName: String) {
        self.sports = []
        super.init(firstName: firstName, lastName: lastName)
    }
 }

Notice how the override keyword isn’t required with required initializers. In its place, the required keyword must be used to make sure that any subclass of StudentAthlete still implements this required initializer.

Here’s a summary of the compiler rules for using designated and convenience initializers:
  • A designated initializer must call a designated initializer from its immediate superclass.
  • A convenience initializer must call another initializer from the same class.
  • A convenience initializer must ultimately call a designated initializer.

All of this from Ray is not exactly new but helps to clarify things. In Xcode the codes would show errors, as they refer to Grade types and other values. 

This is all in the advanced part of Ray's book so I'm not going to worry about the ins and outs - I genuinely get the concepts! I should feel no guilt or shame in getting it and not needing to do more reading! Next time we're looking at 'Semantics' - now that will be something brand new! I wish there were some challenges to do but there's nothing with Treehouse. I've found something called 'Big Nerd Ranch', which I may look at after this Treehouse Intermediate course...

Comments

Popular posts from this blog

*Xcode Project Entry 2* F1 Quiz - part 1

Angela Yu Course Part 10 (up to lesson 112)

Angela Yu Xcode 12 Course - Part 7 (lectures 74 to 79)