Swift basics - classes and structures

Classes and structures
Classes and structures have properties, methods, initialisers, subscripts (provide access to values), and extensions.

Difference between classes and structures:
A class can inherit from parent classes, a structure is unable to. Structures can implement protocols, though.
Structures do not have custom deinitialisers, classes do.
If a class object gets sent to a a function, changes to the object will persist. However, changes to the structure will not persist.

Structures or classes?
Structures make use of less memory overheads, so there are performance gains in using them.


To create a class:
class MyClass
{
   //properties
   let a = 3
   //or let a: Int
   var b = ""
}

Create a structure:
struct MyStruct{

   //properties
   let a = 3
   var b = ""
}

You can able to create an instance of a class and of a structure:
var aStruct = MyStruct()
var aClass = MyClass()
By default, you can give the properties in the structure a value during instantiation.
var aStruct = MyStruct(b: "abc")
OR
var aStruct = MyStrcut(a: 2, b: "abc")

We can access the properties in a class this way:
var x = aClass.a
We can indicate that a class is public:
public class MyClass{}

Within a class or a struct, we can refer to properties using the keyword self:
self.a
self.b

Computed properties:
It is like
var areaOfSquare: Double{
 get{
 return self.length * self.length
}}

They can get/set properties:
var areaOfSquare: Double{
 get{
 return self.length * self.length
}
set(newArea)
{self.length = newArea.squareRoot}
}

OR we can use newValue:
var areaOfSquare: Double{
 get{
 return self.length * self.length
}
set
{self.length = newValue.squareRoot}
}

Computer properties can be used in both classes and structures.
When we instantiate the class or struct, we don't have to add in the computed properties as they are set in the backend, having been computed. In this case, we have set the length, so the area will automatically be computed.

Property observers:
Called whenever a property is set.
willSet is called before the property is set.
didSet is called after the property is set.

For instance:
//We need to declare the type of the property if observers are being used
var areaOfSquare: Double = 0.0
{
  willSet(length){}
  didSet{}
}

We can add methods to classes:
func getArea(length: Int, breadth: Int) -> Int
{
  return length * breadth
}
 OR
func getArea() -> Double
{
  return self.length * self.breadth
}

If a method within a structure modifies a property, the function will need to be mutating:

mutating func getArea() -> Double
{
  return self.length * self.breadth
}

We can create initialisers which are called whenever a class or structure is instantiated:
init()
{}

We can overload the initialiser with multiple versions get different number of parameters:
init(length: Int){}
There is no return type needed.
Classes (not structures) have deinitialisers - what is being called before an instance of a class is destroyed.
deinit(){}
Initialisers can have internal and external parameters:
init(length len: Int){ self.length = len }

Falible initialisers
Some initialisers might fail, let's say, if it relies on an external resource, like a web service that is not available.
init?(length: Int){if self.area < 50{return nil}}
nil is returned when the initialisation has failed.

To call a failable initialiser:
if let squ = SquareClass(length: 3) {//what happens if ok} else {//what happens when failed}

Within a class, we can assign access controls to properties, methods and initialisers:
Open (for framework API), Public (other classes can access), Internal (default), Fileprivate (allow access within the same source file), Private (least accessible)

We cannot mark a method as public when a parameter or return type is private.
We cannot mark a method as public when the class or structure itself is private.


Inheritance:
class Food { var calories = 0.0
func getKJ(calories: Double)
{ return calories/ 4.184}
func getDescription() -> String
{return "Food"}
}

class IceCream: Food{ var flavour = ""}

The class Ice Cream inherited the properties and methods from Food.
We can access var iceCream = IceCream()
var x = iceCream.calories

You can override a method in Food inside Ice cream.
Inside the IceCream class:
override func getDescription() -> String
{ return "Ice Cream"}

We can override properties as well:
Inside the food class:
var description: String{ return "food}
Inside the ice cream class:
override var description:String{ return "\(super.description) is ice cream"}
super.description gets description property from food class.

Overriding the initialiser of Food by subclass IceCream
class IceCream: Food
{
  override init()
  { super.init()}
}

To prevent overriding or subclassing, we can use the final keyword.
Other keywords: final func, final var, final class


If you have 2 classes and ClassA is a property of ClassB and vice versa, it might cause problems with memory management. (Instance of ClassA can't be destroyed until instance of ClassB is destroyed and vice versa. - This will cause a memory leak where the app cannot release its memory properly and cause the app to crash.)
To go around the problem:
class ClassA{ unowned let classB: ClassB}
class ClassB{ var classA : ClassA?}
Unowned means that a class value cannot be nil.

Comments

Popular posts from this blog

Setting up a playground

Go to another page