Study in harmony! Not anxious not impatient!! I am your old friend Xiao Qinglong

In explorations such as the ISA analysis in the previous article (part 1), we were left with two questions: where are member variables and class methods stored? Before answering this question, it is necessary for us to first understand the classification of Memory in iOS app: Clean Memory, Dirty Memory, Compressed Memory.

  • Clean Memory
To save Memory space, a portion of low-priority data is stored in Clean Memory. Some people call it the part that can Page Out.Copy the code
  • Dirty Memory
Refers to the memory that the App has written data to.Copy the code
  • Compressed Memory
When the memory is insufficient, the system compresses the unused memory until the next access time. For example, when we use Dictionary to cache data, we assume that we are already using it3Page memory, when not accessed, may be compressed into1Page, when used again will be decompressed into3Page.Copy the code

Here we are mainly talking about Clean Memor and Dirty Memory. In the last article, when we looked at objc_class, we mentioned class_rw_t, and class_rw_t contains class_ro_t.

  • Class_ro_t: read-only.
Contains attributes, methods, and protocols that the class determines at compile time.Copy the code
  • Class_rw_t is mentioned: readable and writable.
Includes run-time addition methods, such as category addition methods.Copy the code

Let’s look at the structure of class_rw_t again:

struct class_rw_t {
    ...
    const class_ro_t *ro() const{... }void set_ro(const class_ro_t *ro){... }const method_array_t methods() const{... }const property_array_t properties() const{... }const protocol_array_t protocols() const{... }};Copy the code

We previously accessed properties to get the properties, but ultimately found no member variables. Class_ro_t contains attributes that were determined at compile time. Will our member variables exist in ro? Let’s print it out and see:

At this point, we have found the member variable.

So where are the class methods?

Before we answer this question, let’s examine the fact that we printed the DirectionChild class’s methods as instance methods. The instance-class relationship is a bit of an upper/lower level, so we wonder: If instance methods are placed inside the class, are class methods placed in the metaclass above the class? Talk is better than practice, we directly code:

The first authentication mode is – – – – – – – – – – – – LLDB debugging

At this point, we also found the class method.

So why does Apple put class methods inThe metaclassInstead of being in a class like instance methods? Because for the underlying level, it’s either instance methods or class methodsObject methods(a class is also an object), bothAccording to the symbol(method name) to find if you define two names like instance method and class method:

- (void)runAgain; + (void)runAgain;
Copy the code

In this way, when calling the runAgain method, the system cannot distinguish between calling the instance method and calling the class method. So Apple designed a metaclass and threw all the class methods into it, and that solved the problem perfectly.

The second validation method – – – – – – – – – – – – traverses the list of instance methods of the -> class and metaclass

Another way to verify that a class method is in a metaclass is to get a list of methods in the form of an API, then iterate over the method name and print it. We mainly want to iterate over the methods of the class and the methods of the metaclass, which is also a class, so we extract the common part of the code and write it as a method:

/// Print the class method name
/// Parameter: Class Class
void logClassMethod(Class cla)
{
    unsigned int methodsCount = 0;
    Method *me =  class_copyMethodList(cla, &methodsCount);
    for (unsigned int i = 0 ; i < methodsCount; i++) {
        Method methodI = me[i];
        SEL sel =  method_getName(methodI);
        NSString *name =  NSStringFromSelector(sel);
        NSLog(@"method name - %@",name);
    }
    /// Do not forget to free memory here
    free(me);
}

/// Print the metaclass method name
/// Parameter: Class Class
void logClassMetaMethod(Class cla)
{
    const char *className = class_getName(cla);
    Class metaClass = objc_getMetaClass(className);
    /// Take the metaclass as an argument
    logClassMethod(metaClass);
}


int main(int argc, const char * argv[]){.../// Print the class method and metaclass method
    NSLog(@Print class methods -);
    logClassMethod(DirectionChild.class);
    NSLog(@"Print metaclass methods -"); logClassMetaMethod(DirectionChild.class); .return 0;
}

Copy the code

I’ll just post the DirectionChild code for easy reference

@interface DirectionChild:Direction
{
    NSString *bgColorValue;
}

@property (nonatomic,strong) NSString *hobby;
@property (nonatomic,assign) NSInteger runSpeed;
@property (nonatomic,copy) NSString *indexZ;
/ / / method- (void)runSpeedNonTime; + (void)runSpeedWithTime:(NSString *)time; - (void)eatSomethingNonTime; + (void)eatSomethingWithTime:(NSString *)time;
@end

@implementation DirectionChild
-(void)runSpeedNonTime{}
+(void)runSpeedWithTime:(NSString *)time{}
-(void)eatSomethingNonTime{}
+(void)eatSomethingWithTime:(NSString *)time{}
@end
Copy the code

Print the result:

The third verification method: – – – – – – – – – – – – print the instance method address

# Pragma mark -- Thirdvoid logInstanceMethodP(Class cla)
{
    NSLog(@"\n\n Third validation method - "instance method");
    /** Print DirectionChild - whether the class contains the runSpeedNonTime function address */
    Method runM_class = class_getInstanceMethod(cla, @selector(runSpeedNonTime));
    NSLog(@"class -> runSpeedNonTime -> %p",runM_class);
    /** Print whether the DirectionChild - metaclass contains runSpeedNonTime function address */
    Class metaClass = objc_getMetaClass(class_getName(cla));
    Method runM_metaClass = class_getInstanceMethod(metaClass, @selector(runSpeedNonTime));
    NSLog(@"metaClass -> runSpeedNonTime -> %p",runM_metaClass);
    
    /** Print DirectionChild - whether the class contains runSpeedWithTime: function address */
    Method runM_class2 = class_getInstanceMethod(cla, @selector(runSpeedWithTime:));
    NSLog(@"class -> runSpeedWithTime: -> %p",runM_class2);
    /** Print DirectionChild - metaclass containing runSpeedWithTime: function address */
    Class metaClass2 = objc_getMetaClass(class_getName(cla));
    Method runM_metaClass2 = class_getInstanceMethod(metaClass2, @selector(runSpeedWithTime:));
    NSLog(@"metaClass -> runSpeedWithTime: -> %p",runM_metaClass2);
}
Copy the code

Print result:

The fourth verification method: – – – – – – – – – – – – print the class method address

# Pragma mark -- Fourthvoid logClassMethodP(Class cla)
{
    NSLog(@"\n\n Fourth validation mode - "class method");
    /** Print DirectionChild - whether the class contains the runSpeedNonTime function address */
    Method runM_class = class_getClassMethod(cla, @selector(runSpeedNonTime));
    NSLog(@"class -> runSpeedNonTime -> %p",runM_class);
    /** Print whether the DirectionChild - metaclass contains runSpeedNonTime function address */
    Class metaClass = objc_getMetaClass(class_getName(cla));
    Method runM_metaClass = class_getClassMethod(metaClass, @selector(runSpeedNonTime));
    NSLog(@"metaClass -> runSpeedNonTime -> %p",runM_metaClass);
    
    /** Print DirectionChild - whether the class contains runSpeedWithTime: function address */
    Method runM_class2 = class_getClassMethod(cla, @selector(runSpeedWithTime:));
    NSLog(@"class -> runSpeedWithTime: -> %p",runM_class2);
    /** Print DirectionChild - metaclass containing runSpeedWithTime: function address */
    Class metaClass2 = objc_getMetaClass(class_getName(cla));
    Method runM_metaClass2 = class_getClassMethod(metaClass2, @selector(runSpeedWithTime:));
    NSLog(@"metaClass -> runSpeedWithTime: -> %p",runM_metaClass2);
}
Copy the code

Print result:

As you can see, the line for the class actually prints the address of the runSpeedWithTime: method.The class methods of a class should be stored in the metaclass's list of instance methodsYes, but why print at the Class level? weThe Command + single class_getClassMethodGo inside and take a look at the implementation:

Method class_getClassMethod(Class cls, SEL sel)
{
    if(! cls || ! sel)return nil;

    return class_getInstanceMethod(cls->getMeta(), sel);
}
If Class or SEL is null, return nil; The underlying implementation of the class_getClassMethod method is class_getInstanceMethod, and the first argument is a CLS metaclass. 3. This explains why the class and metaclass are the same as the address printed on the image with runSpeedWithTime:. At the bottom there is no concept of class methods, everything is an object, only the concept of each other's methods. * /
Copy the code

The fifth verification method: – – – – – – – – – – – – print IMP address

# Pragma mark -- Number fivevoid logIMP(Class cla)
{
    NSLog(@"\n\n Fifth validation - "IMP");
    /** Print DirectionChild - whether the class contains the runSpeedNonTime function address */
    IMP runM_class = class_getMethodImplementation(cla, @selector(runSpeedNonTime));
    NSLog(@"class -> runSpeedNonTime -> %p",runM_class);
    /** Print whether the DirectionChild - metaclass contains runSpeedNonTime function address */
    Class metaClass = objc_getMetaClass(class_getName(cla));
    IMP runM_metaClass = class_getMethodImplementation(metaClass, @selector(runSpeedNonTime));
    NSLog(@"metaClass -> runSpeedNonTime -> %p",runM_metaClass);
    
    /** Print DirectionChild - whether the class contains runSpeedWithTime: function address */
    IMP runM_class2 = class_getMethodImplementation(cla, @selector(runSpeedWithTime:));
    NSLog(@"class -> runSpeedWithTime: -> %p",runM_class2);
    /** Print DirectionChild - metaclass containing runSpeedWithTime: function address */
    Class metaClass2 = objc_getMetaClass(class_getName(cla));
    IMP runM_metaClass2 = class_getMethodImplementation(metaClass2, @selector(runSpeedWithTime:));
    NSLog(@"metaClass -> runSpeedWithTime: -> %p",runM_metaClass2);
}
Copy the code

Print the result:The two places circled in the picture, as we’ve seen before,IMPIt should benil, where it should show 0x0, but why does it have an address? With that in mind, we click throughclass_getMethodImplementationCheck out the implementation inside:

IMP class_getMethodImplementation(Class cls, SEL sel)
{
    IMP imp;

    if(! cls || ! sel)return nil;

    lockdebug_assert_no_locks_locked_except({ &loadMethodLock });

    imp = lookUpImpOrNilTryCache(nil, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER);

    // Translate forwarding function to C-callable external version
    if(! imp) {return _objc_msgForward;
    }

    return imp;
}
Copy the code

We found that when IMP is nil, it returns the default _objc_msgForward address, which explains why the imp address print above is not empty.

Baidu cloud: link: pan.baidu.com/s/1qWbB4c7l… Password: vkh4

The article has been appended to – – – – – – – – – – – –

  • IOS low-level exploration of isKindOfClass, isMemberOfClass