Runtime Learn to tidy up

object

  • Object initialization exploration
  • OC object initialization supplement and memory alignment exploration
  • The nature of OC objects and ISA

class

  • Class structure exploration
  • Isa-related interview questions
  • Cache_t structure analysis and underlying exploration

The message

  • Runtime Runtime & Method essence & Fast method lookup
  • Objc_msgSend slow lookup
  • Dynamic method resolution

Application loading, class, and class initialization

  • Dyld application loads
  • Load and initialize analysis
  • Objc_init () and read_images analysis
  • Class load analysis
  • Load analysis of classification
  • Class extension and classification (category)

Related Interview Questions

1.loadandinitializeMethod call principles and call order?

  1. The load method

    • loadMethod during application loading (dyld) complete the call atmainFunction before
    • At the bottomload_imagesWhen processing, two are maintainedloadLoad tables, one for the class and the other for the class, with preference for the classloadMethod invocation
    • In the classloadMethod is recursively processed to ensure that the parent class is processed first
    • soloadMethods are called in the order of superclass, subclass, and classification
    • In the classificationloadMethods are called in the order in which they were compiled
  2. The initialize method

    • initializeIt’s called when the first message is sent, soloadbeforeinitializecall
    • The way to classify is in classrealizeafterattachWhat goes in is inserted in the front, so if it’s implemented in the categoryinitializeMethod will be called firstinitializemethods
    • initializeThe internal implementation is message sending, so if the subclass is not implementedinitializeThe superclass will be calledinitializeMethod is called twice
    • Because recursion is used internally, if both the subclass and the superclass implementinitializeMethod, then the parent class will be called first, and the child class will be called

See the concrete underlying implementation principle: Load and initialize analysis

  1. Add c++ constructors

    • After analyzing dyld, you can determine the order of calls, load->c++->main

    • But if c++ is written in an objc project, when objc_init() is called, the c++ function will be called first via the static_init() method instead of waiting until _dyld_objc_notify_register registers load_images with dyld

    • At the same time, dyld is not required if objc_init() starts from itself, and it is possible for c++ functions to be called before load

2. What is Runtime?

  1. RuntimeIs made up ofCandC++A set of assembly implementationsAPIforOCThe language adds object-oriented, runtime functionality
  2. The runtime (Runtime) refers to deferring data type determination from compile time to run time, such as the distinction between class extension and classification
  3. Everyday writtenOCThe code, as the program runs, actually ends up being converted toRuntimetheCLanguage code,Runtime 是 Object-CBehind the scenes

3. The nature of the method,selWhat is?IMPWhat is? What is the relationship between the two?

  1. The essence of the method: To send a message, the message will have the following processes:
    1. Quick lookup (objc_msgSend) ~cache_tThe cache information
    2. Slow lookup ~ recurse itself or a parent class ~lookUpImpOrForward
    3. Message not found: dynamic method parsing ~resolveInstanceMethod
    4. The message is forwarded quickly ~forwardingTargetForSelector
    5. The message is forwarded slowly ~methodSignatureForSelectorforwardInvocation
  2. selIs the method number, inread_imagesIt is compiled into memory
    • typedef struct objc_selector *SEL;
  3. impThat’s our function implementation pointer, findimpIt’s finding the function
  4. selIt’s like the table of contents of a booktittle
  5. impIt’s the page number of the book
  6. To look for specific functions is to look for specific chapters in the book
    1. We first know what we want to seetittle (sel)
    2. According to the corresponding page number of the directory (imp)
    3. Turn to the specific content method implementation

4. Is it possible to add instance variables to the compiled class? Can you add instance variables to classes created at run time?

  1. You cannot add an instance variable to the compiled class

    • Our compiled instance variable is stored atroOnce the compilation is complete, the memory structure is completely determined;
    • You can add methods and properties (associated objects) to classes by classification
  2. Instance variables can be added to classes created at runtime as long as they are not registered in memory

    You can create classes at runtime with objc_allocateClassPair and add member variables and properties to them, as shown in the following code:

    // Create a Class with objc_allocateClassPair const char * className = "SelClass"; Class SelfClass = objc_getClass(className); if (! SelfClass){ Class superClass = [NSObject class]; SelfClass = objc_allocateClassPair(superClass, className, 0); BOOL isSuccess = class_addIvar(SelfClass, "name", sizeof(NSString *)), log2(_Alignof(NSString *)), @encode(NSString *)); class_addMethod(SelfClass, @selector(addMethodForMyClass:), (IMP)addMethodForMyClass, "V@:");Copy the code

[super class] [self class] [super class] [super class]

In the following example, LGTeacher inherits from LGPerson. In the init initialization method of LGTeacher, we call [self class] and [super class].

    // LGPerson
    @interface LGPerson : NSObject
    @end

    @implementation LGPerson
    @end
    
    // LGTeacher
    @interface LGTeacher : LGPerson
    @end

    @implementation LGTeacher
    - (instancetype)init{
        self = [super init];
        if (self) {
           NSLog(@"%@ - %@", [self class], [super class]);
        }
        return self;
    }
    @end

Copy the code

Analysis ideas:

Let’s make sure that LGPerson and LGTeacher do not currently implement a class method, so they will both end up calling NSObject’s instance method class, which is implemented as follows:

    - (Class)class {
        return object_getClass(self);
    }
Copy the code

So both of these methods are going to return the class of self, so who is self? We know when we’re analyzing the nature of the method, the nature of the calling method is to send a message, objc_msgSend, with two hidden parameters, id self and SEL SEL, and the hidden parameter self is the type that we’re analyzing.

  • [self class] the output is LGTeacher. Because the sender of the message is a LGTeacher object, through the message sending mechanism, find NSObejct and call the class method, but the recipient of the message has not changed, it is still a LGTeacher object!

  • What does [super class] output? The same way clang, see CPP in the underlying implementation principle is what?

    The super keyword finally uses the objc_msgSendSuper method in the bottom layer, and its recipient is (ID)self. The global search for objc_msgSendSuper logic is shown in the following figure:

    The objc_super structure is as follows:

    /// Specifies the superclass of an instance. struct objc_super { /// Specifies an instance of a class. __unsafe_unretained _Nonnull id receiver; /// Specifies the particular superclass of the instance to message. #if ! defined(__cplusplus) && ! __OBJC2__ /* For compatibility with old objc-runtime.h header */ __unsafe_unretained _Nonnull Class class; #else __unsafe_unretained _Nonnull Class super_class; #endif /* super_class is the first class to search */ };Copy the code

    Select * from LGTeacher where id receiver is used and Class super_class is used. Select * from LGTeacher where id receiver is used and Class super_class is used. The objc_msgSendSuper method is called internally and the parameter objc_super is passed, where receiver is the LGTeacher object and super_class is the parent class of LGTeacher, which is the first class to look for.

    That is, the recipient of [super Class] is still a LGTeacher object to call the methods of the superclass.

View the running structure:

Supplement:

Call objc_msgSendSuper, but actually call objc_msgSendSuper2 and why?

Search for objc_msgSendSuper globally and enter the assembly implementation process. In the assembly process, objc_msgSendSuper2 will eventually be called, as shown below:

5. A case of pointer translation and message sending?

For example, the LGPerson class has an instance method, saySomething, which can be called in viewDidLoad either by creating a LGPerson object or by bridging it.

- (void)viewDidLoad {
    [super viewDidLoad];
    
    LGPerson *person = [LGPerson alloc];
    [person saySomething];
    
    Class cls = [LGPerson class];
    void  *kc = &cls;
    [(__bridge id)kc saySomething];
}

@implementation LGPerson
- (void)saySomething{
    NSLog(@"%s - %@",__func__);
}
@end

Copy the code

Can the question be invoked successfully?

  1. Analysis methods

    First, the essence of method invocation is to send a message, find the class address through the object isa, carry out address translation, and find the corresponding method to implement IMP through SEL.

    • [person saySomething]; It certainly works that way

      How does this process work? Find the corresponding class using the Person object’s ISA pointer, and shift the address in the class. First, do a quick lookup in cache_t, and if you can’t find it, then look in the method list and the parent class’s method list. In summary, take the address of the class as entry, shift the address, and find the IMP.

    • [(__bridge id)kc saySomething]; Is that ok?

      Class CLS = [LGPerson Class]; What is CLS? CLS is a pointer, and the definition of Class is a pointer to a pointer to objc_class, in this case LGPerson. Assign the address of CLS to KC, where kc is the address of CLS, which also points to the class.

    To sum up, the entry point of the two calls is the same, the method lookup process starts from the same address, it must be called, in addition to the address, person also has the memory data structure; Kc has only one address, which is a disguised Person object, as shown below:

    Through LLDB debugging, we can find that kc points to the class, as shown in the following figure:

    Run validation and both can be called successfully. See below:

  2. To expand case

    To modify the example above, the first property of the LGPerson object in the saySomething method is output in the following code:

    - (void)viewDidLoad {
        [super viewDidLoad];
    
        LGPerson *person = [LGPerson alloc];
        person.kc_name = @"name123";
        [person saySomething];
    
        Class cls = [LGPerson class];
        void  *kc = &cls;
        [(__bridge id)kc saySomething];
    }
    
    @interface LGPerson : NSObject
    @property (nonatomic, copy) NSString *kc_name;
    - (void)saySomething;
    @end
    
    @implementation LGPerson
    - (void)saySomething{
       NSLog(@"%s - %@", __func__, self.kc_name);
    }
    @end
    Copy the code

    Now what happens when we run this?

    • [person saySomething];No questions about the output structure after the call
    • [(__bridge id)kc saySomething];What is the output structure of? See below:

    According to LLDB debugging, person performs address translation to obtain the attribute kc_name. This data structure is in the heap, while KC is just an address. Obtaining kc data structure only outputs its data information in the stack. See below:

Runtime Interview questions, continue to update…