swiftMethod distribution mode

In SWIFT language, there are three ways of method distribution: direct distribution, function table distribution and dynamic distribution.

Distribution Modes

1. Direct distribution

Direct distribution is the most efficient because the method invocation location can be determined at compile time and then called directly, without the complex process of addressing the method. The compiler also makes some optimizations during compilation.

2. Function table distribution

Each object maintains a list of methods, and when a method is called, the corresponding method is found and then called. Compared with direct distribution, there will be two more memory reads and one more addressing process, because first you need to read the pointer to the method list and then jump to the method list, then read the method in the list and find the corresponding method to jump to the implementation, so the efficiency is relatively lower.

3. Dynamic distribution

Dynamic dispatch determines method invocation at run time and is therefore the least efficient. But it provides the building blocks for KVO and Method Swizzling, which is very useful.

Usage scenarios

In my opinion, direct distribution is used when method calls can be determined directly by the compiler. For example, structure is not inheritable, so the method for a structure is also determined, so the structure is directly distributed. All corresponding value types, non-inheritable classes (plus final), extended classes and protocol methods, and private class methods, are uniquely determinable and therefore are distributed directly.

For the function table issue:

class ParentClass {
    func method1() {}
    func method2() {}
}
class ChildClass: ParentClass {
    override func method2() {}
    func method3() {}}Copy the code

The corresponding data structure is as follows:

For dynamic distribution:

class ParentClass {
    dynamic func method1() {}
    dynamic func method2() {}
}
class ChildClass: ParentClass {
    override func method2() {}
    dynamic func method3() {}}Copy the code

Corresponding data structure:

oc
OC

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    class_rw_t *data() { 
        returnbits.data(); }... }Copy the code
struct class_data_bits_t {
    uintptr_t bits;
    
    class_rw_t* data() {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
	// ...
}
Copy the code
struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;

    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;
    // ...
}
Copy the code
struct class_ro_t { uint32_t flags; uint32_t instanceStart; uint32_t instanceSize; / /... const char * name; method_list_t * baseMethodList; protocol_list_t * baseProtocols; const ivar_list_t * ivars; / /... };Copy the code

Rw: readWrite Ro: Readonly. Classes can dynamically add methods, attributes, and protocols, but not variables.

Therefore, the summarized distribution methods are as follows:

Small example

protocol Run { }
extension Run {
    func run() {
        print("run")
    }
}
class Dog: Run {
    func run() {
        print("dog run")}}letDog: Run = dog () dog.run() // Prints: RunCopy the code

Because methods in the protocol extension are distributed directly, dog is declared to follow the Run protocol, so it is determined at compile time that it is calling the Run method in the protocol extension.

reference

www.raizlabs.com/dev/2016/12…