Ray Wenderlich Course Part 12 (up to lesson 110)

Here we go - the last of the 'theoretical' Ray sections of lessons before the next project. I'm not expecting to get all of this, after how tricky the last couple of sections have been. Again, I will type along where I can and keep it at normal speed. 

Classes vs Structures

So here is a simple structure (already typed out for me):

struct PersonStruct {
  var firstName: String
  var lastName: String

  var fullName: String {
    return "\(firstName) \(lastName)"
  }

}

To make a class, it is simple enough. Same as above. 


class PersonClass {
    var firstName: String
    var lastName: String
    
    var fullName: String {
        return "\(firstName) \(lastName)"
    }
}

The difference of course is that inits are needed. I know all about this from before. 

Ray mentions Heap vs Stack views. Functions will on the stack - will be used then gone again. Classes are on the heap, which means that they are still there...they are reference types. 

var person1 = PersonStruct(firstName: "Kiran", lastName: "Johal")

var person2 = person1

person1.firstName = "Anvir"

person2.firstName

A good example of how structs are value types. In the above, person2.firstName still = Kiran!

var person3 = PersonClass(firstName: "Josh", lastName: "Gonet")

var person4 = person3

person4.firstName = "Bob"

person3.firstName

Interestingly, person3 equals Bob too! This is why it is a reference type. All fine. 

So here is the key difference:

STRUCTURES are VALUE types - considered equal if values are equal
CLASSES are OBJECT types - these are unique 

Speed is a big factor - for creating many instances and only exist for a short period of time

If unsure - start with structs, then covert to classes!

All that sounds clear to me. 

Challenge!

## CLASSES VS STRUCTURES

 Imagine you're writing a movie-viewing application in Swift. Users can create lists of movies and share those lists with other users.

 Create a `User` and a `List` class that uses reference semantics to help maintain lists between users.

 - `User` - Has a method `addList(_:)` which adds the given list to a dictionary of `List` objects (using the `name` as a key), and `list(forName:) -> List?` which will return the `List` for the provided name.
 - `List` - Contains a name and an array of movie titles. A `print` method will print all the movies in the list.
 - Create `jane` and `john` users and have them create and share lists. Have both `jane` and `john` modify the same list and call `print` from both users. Are all the changes reflected?
*/

// TODO: Write solution here

class List {
    
    let name: String
    let movieTitles: [String] = []
    
    init(name: String) {
        self.name = name
    }
    
    func printMovies() {
        
        print("Movie List \(name)")
        for movie in movieTitles {
            print(movie)
        }
        print("\n")
    }
}

class User {
    var lists: [String: List] = [:]
    func addList(_ list: List) {
        lists[list.name] = list
    }
    func list(forName name: String) -> List? {
        return lists[name]
    }
}

This was MASSIVELY confusing! I got some but not a lot of it. Very hard to make sense of. 

The next part of the challenge I am skipping over, as it's too complicated. Typical!

Your challenge here is to imagine a set of objects to support a t-shirt store. Decide if each object should be a class or a struct, and why. You don't need to write any code; just decide whether to use a class or a struct for each.

 - `TShirt` - Represents a shirt style you can buy. Each `TShirt` has a size, color, price, and an optional image on the front.
 - `User` - A registered user of the t-shirt store app. A user has a name, email, and a `ShoppingCart` (below).
 - `Address` - Represents a shipping address, containing the name, street, city, and zip code.
 - `ShoppingCart` - Holds a current order, which is composed of an array of `TShirt` that the `User` wants to buy, as well as a method to calculate the total cost. Additionally, there is an `Address` that represents where the order will be shipped.


OK, here goes!

TShirt - struct. Seems simple enough to have this as a value type. 

User - I would go for class because people are more eomplex and the reference type makes more sense here

Address - struct. Simple, clear information lends itself to this. 

ShoppingCart - class, just because there are so many different elements to it and it would make more sense to be a reference type....

Solution:

 - TShirt: A TShirt can be thought of as a value, because it doesn't represent a real shirt, only a description of a shirt. For instance, a TShirt would represent "a large green shirt order" and not "an actual large green shirt". For this reason, TShirt can be a struct instead of a class.
 - User: A User represents a real person. This means every user is unique so User is best implemented as a class.
 - Address: Addresses group multiple values together and two addresses can be considered equal if they hold the same values. This means Address works best as a value type (struct).
 - ShoppingCart: The ShoppingCart is a bit tricker. While it could be argued that it could be done as a value type, it's best to think of the real world semantics you are modeling. If you add an item to a shopping cart, would you expect to get a new shopping cart? Or put the new item in your existing cart? By using a class, the reference semantics help model real world behaviors. Using a class also makes it easier to share a single ShoppingCart object between multiple components of your application (shopping, ordering, invoicing, ...).
 */

So I basically aced that! I've got the key understanding it seems, but putting the code into practice is another story!

Inheritance

When one class inherits functionality from another. 

Good examples here:

struct Grade {
  var letter: Character
  var points: Double
  var credits: Double
}

class Person {
  var firstName: String
  var lastName: String

  init(firstName: String, lastName: String) {
    self.firstName = firstName
    self.lastName = lastName
  }
}

class Student: Person {
  var grades: [Grade] = []

  func recordGrade(_ grade: Grade) {
    grades.append(grade)
  }
}

So student has everything from person plus whatever is added. 

So SUBCLASS for the class that has inherited. 
BASE CLASS or SUPERCLASS for the original/one that has been inherited from. 

class BandMember: Student {
    var minimumPracticeTime = 2
}

class OboePlayer: BandMember {
    override var minimumPracticeTime: Int {
        get {
            return super.minimumPracticeTime
        }
        set {
            super.minimumPracticeTime = newValue / 2
        }
    }
}

Good examples to show many classes and the override for one of the variables. 

OboePlayer is CHILD of BandMember
BandMember is PARENT of OboePlayer

Polymorphism - treating objects differently based on context. 

Downcasting - using as? as a way of unwrapping to see if a let/var is of a certain type - e.g. a superclass of its initial declaration. 

So 'as' on its own is 'up-classing' - moving to a more parent class
As? would be downcast
As! would be FORCED downcast

Class hierarchy - makes sense

Initializers

I always found these tedious with the courses with Pasan as I found myself doing inits for everything, including subclasses which didn't even seem to change!

It's since been clarified that I don't need to do inits unless they have CHANGED from the parent class!

So anything without an initial value needs to be initialised. Fair enough. 

Phase 1 - you must initialise all of the stored properties. You can't do any functions etc. until these have been initialised. 

Phase 2  -  call any methods once inits have been correctly set up

You can use 'required' as an alternative init. It's like override. 
Convenience - shortcut. Another way to call an init. 

Not too sure when to use these!

OK, Designated Init - must be called from the parent class

Create a class named `Animal` that has a single stored property named `name`, that is a `String`. It should have a required initializer that takes `name` as a parameter, and a function `speak()` that does nothing.
*/

// TODO: Write solution here

class Animal {
    
    let name: String
    
    required init(name: String) {
        self.name = name
        func speak() {
            
        }
    }
}

This was all good but I can't figure out the next bit.....


class Dog: Animal {
    
    let numTricksLearned: Int
    
    required init(name: String) {
        numTricksLearned = 0
        super.init(name: name)
        speak()

        }


        override func speak() {
        print("Bow Wow!")
    }
}

Well I wasn't too far away!

 In an extension, add a convenience initializer to `Dog` that defaults the dog's name to your favorite dog's name, with however many tricks the dog has learned.
*/

// TODO: Write solution here

extension Dog {
    
convenience init() {
    self.init(name: "George", numTricksLearned: 4)
    
}

}

Again, a lot of this was familiarisation with the syntax etc. 

When Should you Subclass?

Basically you can always choose this.

Things to keep in mind:
Each class should only have ONE concern
Type safety (could get confusing with lots)
Shared base classes - again a lot to monitor
Extensibility - not sure about this one
Protocols may be better! Linked to identity

Memory Management

Heap - Ray mentioned this before. 

Reference count - OK so if the same value is referred to then the reference count goes up by one. A variable or a constant. 

ARC - automatic reference counting

If you make the value of that referred to variable nil, then the reference count goes down. 

Weak - must be an optional type. That links to what I had before with labels on Xcode! So it's to avoid the reference counting? Sort of get it. 

There we go! That was generally better than Structures and MUCH easier than collections. I definitely have picked up some useful key points but am in a rush so will leave it here. At some point tomorrow - possibly on a train or in the evening before bed - I  will be going on to the next Xcode project. Apparently it's with a guy called Brian! Now, I will have to be ruthless here and decide within a few lessons if it's worse continuing here, or giving another course a go with some more hands on practice. If I can't find decent Udemy ones that I can follow then I will go back to Angela's - she's been the best so far! I will leave Treehouse for now as I don't feel equipped enough to call myself an 'Intermediate!'. 


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)