Method

The basic concept

Enumerations, structs, and classes can all define instance methods and type methods

  • Instance Method: called from the Instance object
  • Type Method: called by Type

Instance method call

class Car {
    var count = 0
    
    func getCount() -> Int {
        count
    }
}

let car = Car()
car.getCount()
Copy the code

Type methods are defined with the static or class keyword

class Car {
    static var count = 0
    
    static func getCount() -> Int {
        count
    }
}

Car.getCount()
Copy the code

Instance properties cannot be called from a type method, and vice versa

Both type methods and instance methods contain the hidden argument self

Self represents the instance object in the instance method

Self represents a type in a type method

Static func getCount() -> Int {self.count}Copy the code

mutating

Structs and enumerations are value types, and by default, the properties of value types cannot be modified by their own instance methods

Mutating in front of the func keyword allows this modification behavior

Struct Point {var x = 0.0, y = 0.0 mutating func moveBy(deltaX: Double, deltaY: Double) {x += deltaY y += deltaY}}Copy the code
enum StateSwitch {
    case low, middle, high
    
    mutating func next() {
        switch self {
        case .low:
            self = .middle
        case .middle:
            self = .high
        case .high:
            self = .low
        }
    }
}
Copy the code

@discardableResult

Adding @discardableresult in front of func eliminates the warning that the return value is not used after a function call

Struct Point {var x = 0, y = 0} struct Point {var x = 0, y = 0} Double) -> Double { x += deltaX return x } } var p = Point() p.moveX(deltaX: 10)Copy the code

Subscript

The basic concept

You can use subscript to add subscripting to any type (enumeration, structure, class)

Some places also translated as: subscript script

Subscript syntax is similar to instance methods, computed properties, and is essentially methods (functions)

Class Point {var x = 0.0, y = 0.0 subscript(index: Int) -> Double { set { if index == 0 { x = newValue } else if index == 1 { y = newValue } } get { if index == 0 { return X} else if index == 1 {return y} return 0}} var p = Point() p[0] = 11.1 p[1] = 22.2 print(p.x) // 11.1 Print (p.y) / / print (p [0]) 22.2/11.1 / print / / 22.2 (p [1])Copy the code

The return value type defined in subscript determines the return value type in the getter and the newValue type in the setter

Subscript can accept multiple arguments of any type

class Grid { var data = [ [0, 1 ,2], [3, 4, 5], [6, 7, 8] ] subscript(row: Int, column: Int) -> Int { set { guard row >= 0 && row < 3 && column >= 0 && column < 3 else { return } data[row][column] = newValue } get { guard row >= 0 && row < 3 && column >= 0 && column < 3 else { return 0 } return data[row][column] } } } var grid  = Grid() grid[0, 1] = 77 grid[1, 2] = 88 grid[2, 0] = 99Copy the code

Subscripts may not have setters, but must have getters, as well as calculating properties

Class Point {var x = 0.0, y = 0.0 subscript(index: Int) -> Double { get { if index == 0 { return x } else if index == 1 { return y } return 0 } } }Copy the code

Subscript can omit getters if it only has getters

Class Point {var x = 0.0, y = 0.0 subscript(index: Int) -> Double { if index == 0 { return x } else if index == 1 { return y } return 0 } }Copy the code

Subscript can set parameter labels

Only calls with custom labels are required to write parameter labels

Class Point {var x = 0.0, y = 0.0 subscript(index I: Int) -> Double {if I == 0 {return x} else if I == 1 {return y} return 0} var p = Point() p.y = 22.2 Print (p [index: 1)) / / 22.2Copy the code

Subscript can be a type method

class Sum {
    static subscript(v1: Int, v2: Int) -> Int {
        v1 + v2
    }
}

print(Sum[10, 20]) // 30
Copy the code

Analyze by disassembly

Looking at the sample code below, we hit the breakpoint at the point on the diagram and then watch the disassembly

When you look inside it, you call the setter to do the calculation

And then I’m going to hit the break point here

See inside it is calledgetterTo calculate

This analysis proves that subscript is essentially a method call

Structure and class are compared as return values

Take a look at the sample code below

struct Point { var x = 0, y = 0 } class PointManager { var point = Point() subscript(index: Int) -> Point {set {Point = newValue} get {Point}} var PM = PointManager() PM [0]. X = 11 11, y: PM [0]. Y) PM. [0] = 22 y / / equivalent to the PM [0] = Point (x: PM [0]. X, y: 22)Copy the code

If we comment out the setter, the call will report an error

But if we replace the structure with a class, we won’t get an error

The reason is that the structure is a value type, and the Point structure obtained by the getter is only a temporary value (can be thought of as a calculated property), and is not really a stored property Point, so an error will be reported

It can also be seen from printing that the point to be modified is not the same address value

The Point class is a pointer variable that you get from the getter, and you modify the property that points to the Point in the heap, so no error is reported

Inheritance by Inheritance

The basic concept

Value types (structs, enumerations) do not support inheritance; only classes do

A class without a parent class is called a base class

Swift does not stipulate that any class must eventually inherit from a base class, as OC and Java do

Subclasses may override subscripts, methods, and attributes of their parent class

class Car {
    func run() {
        print("run")
    }
}

class Truck: Car {
    override func run() {
        
    }
}
Copy the code

Memory structure

Look at the memory footprint of the following classes

class Animal {
    var age = 0
}

class Dog: Animal {
    var weight = 0
}

class ErHa: Dog {
    var iq = 0
}

let a = Animal()
a.age = 10
print(Mems.size(ofRef: a)) // 32
print(Mems.memStr(ofRef: a))

//0x000000010000c3c8
//0x0000000000000003
//0x000000000000000a
//0x000000000000005f

let d = Dog()
d.age = 10
d.weight = 20
print(Mems.size(ofRef: d)) // 32
print(Mems.memStr(ofRef: d))

//0x000000010000c478
//0x0000000000000003
//0x000000000000000a
//0x0000000000000014

let e = ErHa()
e.age = 10
e.weight = 20
e.iq = 30
print(Mems.size(ofRef: e)) // 48
print(Mems.memStr(ofRef: e))

//0x000000010000c548
//0x0000000000000003
//0x000000000000000a
//0x0000000000000014
//0x000000000000001e
//0x0000000000000000
Copy the code

First, there are 16 bytes inside the class to store the class information and reference count, followed by the attributes, and since the principle of heap space allocation is a multiple of 16, so the memory footprint is 32, 32, 48 respectively

A subclass inherits attributes from its parent class, so memory counts toward the parent class’s attribute storage

Overrides instance methods, subscripts

class Animal {
    func speak() {
        print("Animal speak")
    }
    
    subscript(index: Int) -> Int {
        index
    }
}

var ani: Animal
ani = Animal()
ani.speak()
print(ani[6])

class Cat: Animal {
    override func speak() {
        super.speak()
        
        print("Cat speak")
    }
    
    override subscript(index: Int) -> Int {
        super[index] + 1
    }
}

ani = Cat()
ani.speak()
print(ani[7])
Copy the code

Class – modified type methods and subscripts that can be overridden by subclasses

class Animal {
    class func speak() {
        print("Animal speak")
    }
    
    class subscript(index: Int) -> Int {
        index
    }
}


Animal.speak()
print(Animal[6])

class Cat: Animal {
    override class func speak() {
        super.speak()
        
        print("Cat speak")
    }
    
    override class subscript(index: Int) -> Int {
        super[index] + 1
    }
}

Cat.speak()
print(Cat[7])
Copy the code

Type methods and subscripts that are static are not overridden by subclasses

However, class methods, subscripts, and subclass overrides allow static

class Animal {
    class func speak() {
        print("Animal speak")
    }
    
    class subscript(index: Int) -> Int {
        index
    }
}


Animal.speak()
print(Animal[6])

class Cat: Animal {
    override static func speak() {
        super.speak()
        
        print("Cat speak")
    }
    
    override static subscript(index: Int) -> Int {
        super[index] + 1
    }
}

Cat.speak()
print(Cat[7])
Copy the code

Subclasses after that are not allowed

Rewrite attributes

Subclasses can override attributes of their parent class (storage, computation) as computed attributes

class Animal {
    var age = 0
}

class Dog: Animal {
    override var age: Int {
        set {
            
        }
        
        get {
            10
        }
    }
    var weight = 0
}
Copy the code

However, a subclass may not override a property of its parent class as a storage property

Only the var attribute can be overridden, not the let attribute

When overridden, the attribute name and type must be the same

The property permissions overridden by a subclass cannot be less than those of its parent class

  • If the parent property is read-only, then the property overridden by the subclass is also read-only and can be read and write
  • If the parent property is readable and writable, the property overridden by the subclass must also be readable and writable

Override instance properties

Var diameter: Int = 0 var diameter: Int { set(newDiameter) { print("Circle setDiameter") radius = newDiameter / 2 } get { print("Circle getDiameter") return  radius * 2 } } } class SubCircle: Circle { override var radius: Int { set { print("SubCircle setRadius") super.radius = newValue > 0 ? newValue : 0 } get { print("SubCircle getRadius") return super.radius } } override var diameter: Int { set { print("SubCircle setDiameter") super.diameter = newValue > 0 ? newValue : 0 } get { print("SubCircle getDiameter") return super.diameter } } } var c = SubCircle() c.radius = 6 print(c.diameter) c.diameter = 20 print(c.radius) //SubCircle setRadius //SubCircle getDiameter //Circle getDiameter //SubCircle getRadius  //12 //SubCircle setDiameter //Circle setDiameter //SubCircle setRadius //SubCircle getRadius //10Copy the code

Storage properties inherited from a parent class are allocated memory, regardless of whether they are later overridden as computed properties

If the setter and getter in the overridden method doesn’t say super, then it will loop forever

class SubCircle: Circle {
    override var radius: Int {
        set {
            print("SubCircle setRadius")
            radius = newValue > 0 ? newValue : 0
        }

        get {
            print("SubCircle getRadius")
            return radius
        }
    }    
}
Copy the code

Override type attribute

A computed type property that is class modified and can be overridden by subclasses

Static var radius: Int = 0 class var diameter: Int { set(newDiameter) { print("Circle setDiameter") radius = newDiameter / 2 } get { print("Circle getDiameter") return  radius * 2 } } } class SubCircle: Circle { override static var diameter: Int { set { print("SubCircle setDiameter") super.diameter = newValue > 0 ? newValue : 0 } get { print("SubCircle getDiameter") return super.diameter } } } Circle.radius = 6 print(Circle.diameter) Circle.diameter = 20 print(Circle.radius) SubCircle.radius = 6 print(SubCircle.diameter) SubCircle.diameter = 20 print(SubCircle.radius) //Circle getDiameter //12 //Circle setDiameter //10 //SubCircle getDiameter //Circle getDiameter  //12 //SubCircle setDiameter //Circle setDiameter //10Copy the code

Type properties (computed, stored) that are modified static cannot be overridden by subclasses

Attribute viewer

Attribute observers can be added to subclasses for superclass attributes (except read-only computed attributes and let attributes)

It’s still a stored property, not a calculated property

class Circle {
    var radius: Int = 1
}

class SubCircle: Circle {
    override var radius: Int {
        willSet {
            print("SubCircle willSetRadius", newValue)
        }
        
        didSet {
            print("SubCircle didSetRadius", oldValue, radius)
        }
    }
}

var circle = SubCircle()
circle.radius = 10

//SubCircle willSetRadius 10
//SubCircle didSetRadius 1 10
Copy the code

If the parent class has an attribute observer, then the subclass will call its own attribute observer willSet first and then the parent class’s attribute observer willSet. And inside the parent class you’re actually doing the assignment, and then the parent’s didSet, and then you’re calling your own didSet

class Circle {
    var radius: Int = 1 {
        willSet {
            print("Circle willSetRadius", newValue)
        }
        
        didSet {
            print("Circle didSetRadius", oldValue, radius)
        }
    }
}

class SubCircle: Circle {
    override var radius: Int {
        willSet {
            print("SubCircle willSetRadius", newValue)
        }
        
        didSet {
            print("SubCircle didSetRadius", oldValue, radius)
        }
    }
}

var circle = SubCircle()
circle.radius = 10

//SubCircle willSetRadius 10
//Circle willSetRadius 10
//Circle didSetRadius 1 10
//SubCircle didSetRadius 1 10
Copy the code

You can add a property observer to the computed properties of the parent class

class Circle {
    var radius: Int {
        set {
            print("Circle setRadius", newValue)
        }
        
        get {
            print("Circle getRadius")
            return 20
        }
    }
}

class SubCircle: Circle {
    override var radius: Int {
        willSet {
            print("SubCircle willSetRadius", newValue)
        }
        
        didSet {
            print("SubCircle didSetRadius", oldValue, radius)
        }
    }
}

var circle = SubCircle()
circle.radius = 10

//Circle getRadius
//SubCircle willSetRadius 10
//Circle setRadius 10
//Circle getRadius
//SubCircle didSetRadius 20 20
Copy the code

Circle getRadius will be called once for printing above because it will get its oldValue before setting the value, so it needs to call the getter once

To test this, we remove the oldValue fetch and print out that there is no call to the getter for the first time

final

Methods, subscripts, and properties modified by final are prohibited from being overridden

Classes modified by final are prohibited from being inherited

The nature of a method call

Let’s take a look at the following example code and examine the difference between a struct and a class calling method

struct Animal {
    func speak() {
        print("Animal speak")
    }
    
    func eat() {
        print("Animal eat")
    }
    
    func sleep() {
        print("Animal sleep")
    }
}

var ani = Animal()
ani.speak()
ani.eat()
ani.sleep()
Copy the code

After disassembly, the method invocation of the discovered structure is called directly by finding the address of the method

The method addresses of structures are fixed

Now let’s look at the implementation of disassembly with a class

class Animal {
    func speak() {
        print("Animal speak")
    }
    
    func eat() {
        print("Animal eat")
    }
    
    func sleep() {
        print("Animal sleep")
    }
}

var ani = Animal()
ani.speak()
ani.eat()
ani.sleep()
Copy the code

After disassembly, it is found that the address of the method to be called is uncertain, so any call to a fixed address will not be a call to a method of the class

In addition, the above calls are all called from the RCX offset 8 bytes to the higher address, which means that the method addresses are consecutive

So what do we do before we call the method

Through disassembly, we can see that the pointer to the global variable will find the storage space of the class in the heap memory to which it points, and then know the address of the method to be called according to the class information in the first 8 bytes of the class, offset from the address of the class information to find the method address, and then call

Then let’s modify the sample code and see what it is

class Animal {
    func speak() {
        print("Animal speak")
    }
    
    func eat() {
        print("Animal eat")
    }
    
    func sleep() {
        print("Animal sleep")
    }
}

class Dog: Animal {
    override func speak() {
        print("Dog speak")
    }
    
    override func eat() {
        print("Dog eat")
    }
    
    func run() {
        print("Dog run")
    }
}

var ani = Animal()
ani.speak()
ani.eat()
ani.sleep()

ani = Dog()
ani.speak()
ani.eat()
ani.sleep()
Copy the code

When subclasses are added, the list of methods in Dog’s class information contains the overwritten parent methods, as well as its own new methods

class Dog: Animal {
    func run() {
        print("Dog run")
    }
}
Copy the code

If the subclass does not override the parent method, the list of methods in the class information contains the parent method, as well as its new method