Question 1: When is the load method called?

A: Call the load method inside load_images.

  • Collect the class load method toloadable_classesTable;
  • The load method for collecting categories is added to the loadable_categories table,
  • Call call_load_methods and internally call the load methods in the two load tables.


(See article: App Loading Process for iOS Analytics)

When is load_images called?

A: Dyly calls load_images when it detects a new image file.

Which method is called first, load or initalize?

A: The load method is earlier.

  • The load method calls load_images when dyly links the image file. Load_images then calls the load method for all classes and categories.

  • Initalize is an initial call made the first time a message is sent to an object.

Q4: load, initalize, C++, which of these three automatic (automatic call) methods call first?

A: look where the C++ definition is.

  • C++ method if written in objc project, then the order of execution: C++ method -> load method -> initalize method

  • C++ method if written in their own project, then the order of execution: load method -> c++ method -> initalize method

Note: for c++ function calls in the objc project, search the objc source code for _objc_init and you’ll see that the static_init function is called internally. The static_init function is an initialization of a C++ function static function.

Q5: What is Runtime? Is it the bottom layer?

A: Runtime is a runtime mechanism. It is a set of APIS compiled in C or C++. It is a feature of OC object-oriented runtime. Application of scene scene:

  • The loading of Category classification attributes and methods;

  • AddMethod (dynamic addMethod);

Runtime allows properties and methods of a class to be dynamically bound to the class after compilation.

Can I add instance variables to the compiled class? Can I add instance variables to classes created at run time?

A: You cannot add instance variables to a compiled class; Classes that are not registered in memory can also be added.

  • Compiled instance variables reside in RO, and once compiled, the memory structure is completely determined and cannot be changed.
  • Here’s a case study and derivation for adding instance variables to classes created at runtime:

When we create the class at run time, the code looks like this:

Class Person = objc_allocateClassPair(NSObject.class, "Person".0);

class_addIvar(Person, "_name", sizeof(NSString *), log2(sizeof(NSString *)), "@");

objc_registerClassPair(Person);
Copy the code

Three main functions are used:

  • Objc_allocateClassPair (Gets the member variable Ivar)
  • Class_addIvar (Add member variables dynamically)
  • Objc_registerClassPair (The registered class)

And “class_addIvar” is executed after objc_allocateClassPair and before objc_registerClassPair. As for why this is the case, we can analyze it through the source code:

First of all, what does objc_allocateClassPair do?
Class objc_allocateClassPair(Class superclass, const char *name, 
                             size_t extraBytes){... objc_initializeClassPair_internal(superclass, name, cls, meta); . }Copy the code

Enter the objc_initializeClassPair_internal:

static void objc_initializeClassPair_internal(Class superclass, const char *name, Class cls, Class meta){... cls_rw_w->flags = RW_CONSTRUCTING | RW_COPIED_RO | RW_REALIZED | RW_REALIZING; meta_rw_w->flags = RW_CONSTRUCTING | RW_COPIED_RO | RW_REALIZED | RW_REALIZING | RW_META; . }Copy the code

We can see that the objc_allocateClassPair on the flag is marked RW_CONSTRUCTING.

What did class_addIvar do?
BOOL 
class_addIvar(Class cls, const char *name, size_t size, 
              uint8_t alignment, const char *type)
{
    if(! cls)return NO; // Non-null judgment.if (cls->isMetaClass()) {// if it is a metaclass, it cannot be added
            return NO;
    }
    // If the class is already assigned but not registered, skip judgment
    if(! (cls->data()->flags & RW_CONSTRUCTING)) {return NO;
    }
    // If the tIvar already exists in ro, skip the judgment
    if ((name  &&  getIvar(cls, name))  ||  size > UINT32_MAX) {
        returnNO; }... }CLS has a value * 2, CLS is not a metaclass * 3, CLS is allocated but not registered * 4, tIvar does not exist in ro */
Copy the code

CLS ->data()->flags when does this change?

What does objc_registerClassPair do?
void objc_registerClassPair(Class cls){...// CLS is allocated and registered, or CLS ISA points to allocated and registered
    if ((cls->data()->flags & RW_CONSTRUCTED)  ||
    (cls->ISA()->data()->flags & RW_CONSTRUCTED)) 
    {
        _objc_inform("objc_registerClassPair: class '%s' was already "
                     "registered!", cls->data()->ro()->getName());
        return;
    }
    // If (non) CLS is allocated but not registered, or (non) CLS ISA points to allocated but not registered
    if(! (cls->data()->flags & RW_CONSTRUCTING) || ! (cls->ISA()->data()->flags & RW_CONSTRUCTING)) { _objc_inform("objc_registerClassPair: class '%s' was not "
                     "allocated with objc_allocateClassPair!", 
                     cls->data()->ro()->getName());
        return;
    }
    /// the state is changed to RW_CONSTRUCTED and the previous marker is clearedcls->ISA()->changeInfo(RW_CONSTRUCTED, RW_CONSTRUCTING | RW_REALIZING); cls->changeInfo(RW_CONSTRUCTED, RW_CONSTRUCTING | RW_REALIZING); . }Copy the code

So, to execute objc_registerClassPair, you need to be allocated but not registered. Once the objc_registerClassPair function is executed, the flag becomes RW_CONSTRUCTED and class_addIvar cannot be executed.

Conclusion:
  • Objc_allocateClassPair (flag is set toRW_CONSTRUCTING)
  • Class_addIvar (one of the conditions executed is flagRW_CONSTRUCTING)
  • Objc_registerClassPair (flag is set toRW_CONSTRUCTED)

What is the difference between [self class] and [super class]?

A: First, let’s write a test case for print:

/// directionChild.m
- (instancetype)init
{
    self = [super init];
    if (self) {
        NSLog(@[super class] ---------%@->%@,[self class], [super class]);
    }
    return self;
}
Copy the code
/ / / the main function
int main(int argc, const char * argv[]) {
    DirectionChild *childClass = [[DirectionChild alloc] init];
}
Copy the code

We find that both prints have the same result. why is this?

Back to objC source code project, command+ standalone into the class method:

Check the object_getClass:

Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}
Copy the code

So the first self printing DirectionChild is self-evident.

Next, [super Class],

Super doesn’t know where it came from, but let’s take a look at the compiled source code in Clang. The instructions are as follows:

  • CD to the upper folder directory of the specified file
  • $xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc DirectionChild.m

Open the resulting DirectionChild.mm and search for “DirectionChild” for quick location:

[super class] :

((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("DirectionChild"))}, sel_registerName("class"));
Copy the code

We find that [super class] actually calls objc_msgSendSuper,

Next, we use Debug to check the actual condition of the runtime:

Currently running is the MAC project, so the real machine running, is the same? Here, we create a new test project and run it to see the effect:

Clang is called objc_msgSendSuper statically compiled, but is called objc_msgSendSuper2 dynamically executed.

To explore this change, let’s

Objc source project global search objc_msgSendSuper:

The current file searches L_objc_msgSendSuper2_body and finds it in _objc_msgSendSuper2:

Returning to the previous question, [super class] prints:

((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self,
(id)class_getSuperclass(objc_getClass("DirectionChild"))},
sel_registerName("class"));
Copy the code

To simplify the code:

((void *)objc_msgSendSuper)((__rw_objc_super){
            (id)self,
        (id)class_getSuperclass(objc_getClass("DirectionChild"))
        },sel_registerName("class"));
Copy the code

The first argument is self, and as we’ve seen before, class ultimately returns the isa reference to the first argument of class, self’s ISA reference to the DirectionChild class, So both [self class] and [super Class] print DirectionChild.

Self =[super init] : self=[super init] : self=[super init]

A: This is to better inherit some of the common attributes, methods, protocols, and so on defined by the parent class.

Question 8: Will the following main function report an error?

// Direction.h
#import <Foundation/Foundation.h>
@interface Direction : NSObject
@property (nonatomic , strong) NSString *hobby;
@end

// Direction.m
@implementation Direction
- (void)run{
    NSLog(@"run faster.");
}
@end

// main.m
int main(int argc, const char * argv[]) {

    Direction *dt = [Direction alloc];
    [dt run];

    Class cls = [Direction class];
    void *ssj = &cls;
    [(id)ssj run];
    
    return 0;
}
Copy the code

Run code:

Turns out, it worked, and the console printed something. So why is it possible to call an instance method of a class without instantiating the object?

  1. First of all, we need to know,Method callIn essence itisSending of messagesobjc_msgSend.

  2. The first parameter of objc_msgSend isreceverMessage receiver,Use it to find the Class Class.

  3. And then from Class data,throughThe second argument to objc_msgSendSELTo find the correspondingIMPAnd call.

  4. dtAs a Direction instance object, through its ISAYou can find the Direction class.

  5. ssjA pointer to an address of the Direction class,You can find the Direction class.

  6. So for objc_msgSend, the first argument is either DT or SSJ, as long as the Class can be found.
  • For instance methods stored in classes, see article: Exploring isa analysis and the like (part 1)

  • For the implementation of objc_msgSend, see articles such as “iOS Underlying Analysis” – Cache Insert, objc_msgSend

Here are “dt” and “SSJ” pointing to the Direction class:

Expansion: Modify the RUN method to view the printed data.

// Direction.m
@implementation Direction
- (void)run{
    NSLog(@"run faster.->%@",self.hobby);
}
@end
Copy the code

Run:

  • The first one prints NULL, because hobby is not assigned;

  • The second reason why the Direction class is printed?

A: Dt as an instance object, alloc opens up space. It has pointer addresses and memory. SSJ, on the other hand, is a pure pointer to the Direction address, and the system does not allocate memory for it. So hobby points to the next address of the SSJ pointer address (in stack frames, for example, dt). Let’s print through the console and see what happens to the data:

Then break the Direction run method:

At this point, we have a guess:

  • Access class attributes through the class address pointer SSJ, the resulting data may be related to the SSJ pointer on the stack frame.


// Direction.h
#import <Foundation/Foundation.h>
@interface Direction : NSObject
@property (nonatomic , strong) NSString *firendNames;
@property (nonatomic , strong) NSString *firendNames2;
@property (nonatomic , strong) NSString *hobby;
@end


// ViewController.m
- (void)viewDidLoad {
    [super viewDidLoad];

    Direction *dt = [Direction alloc];
    [dt run];
    // add three instance variables zo1, zo2, zo3
    Zoon *zo1 = [Zoon new];
    Zoon *zo2 = [Zoon new];
    Zoon *zo3 = [Zoon new];
    
    Class cls = [Direction class];
    void *ssj = &cls;
      [(__bridge id)ssj run];
}

@end
Copy the code

Continue to repeat the console print above:

Break point under Direction run:

The print results verify the above conjecture:

  • Access class attributes through the class address pointer SSJ, the obtained data is related to the SSJ pointer on the stack frame, according to the order of printing the stack.

So how does a DT instance object access the Hobby property? Find the address of the property by panning OBJC_IVAR_ XXXX_XXXX_XXX at the beginning of the address.

Question 9: What is the sequence of the following codes?

Structure stack
- (void)viewDidLoad {
    [super viewDidLoad];
    /// test the insertion order
    Direction *dt1 = [Direction alloc];

    struct SSJStruct sSJStruct = {@1The @22};
    
    Direction *dt2 = [Direction alloc];
    NSLog(@"End of test.");
}
Copy the code

A:

  • Dt1, sSJStruct, dT2, from high address to low address stack.

  • Inside sSJStruct, low address pushes high address.

Run code:

Arguments stack
// Custom functions
int ssj_fun(id obj1,id obj2){
    return 0;
}

- (void)viewDidLoad {
    [super viewDidLoad];

    /// test parameter push
    Direction *dt1 = [Direction alloc];

    Direction *dt2 = [Direction alloc];
    
    ssj_fun(dt1, dt2);
    NSLog(@"End of test.");
}
Copy the code

Run the code at breakpoint:

Print dt1, dt2, obj1, and obj2 respectively, where dt1 and dt2 are pointer addresses, and obj1 and obj2 are value addresses. The stack is pushed from high to low, starting with obj1 and then obj2.

Does super in [super viewDidLoad] pass the current Controller or the parent of the current Controller?

// ViewController.m
- (void)viewDidLoad {
    [super viewDidLoad]
    // The actual run will call objc_msgSendSuper2
    // objc_msgSendSuper2 is defined as follows
    objc_msgSendSuper2(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
}
Copy the code

So, does super here pass ViewController or UIViewController? With this question in mind, we are going to actually print it:

The next symbolic breakpoint objc_msgSendSuper2

Enter register read to view the register

Console, print x0, the data for the first parameter:

Code:

Link: pan.baidu.com/s/1uPGdFij0… Password: IL7H — from Baidu network disk super member V2 share