The previous two articles recorded some knowledge points and common applications of learning Runtime respectively. I set up a flag that I was going to write three articles about Runtime, and this was the result.

This article intends to use what has been learned to answer two interview questions for admission exams at ObjC Runtime, Sunnyxx’s neurological hospital.

Question 1: What does the following code output?

@implementation Son : Father
- (id)init {
    self = [super init];
    if (self) {
        NSLog(@"% @", NSStringFromClass([self class]));
        NSLog(@"% @", NSStringFromClass([super class]));
    }
    return self;
}
@end
Copy the code

Result: Son/Son

Analysis:

For the above answer, the first result should be the expected result, but the second result is confusing.

Then we use the knowledge points discussed in the previous article to analyze the whole process.

Because neither Son nor Father implements the -(Class)calss method, all calls here will eventually find the -(Class)calss method in the base Class NSObject. So what we need to know is the implementation of this method in NSObject.

An implementation of the -(Class) Class can be found in nsobject.mm:

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

Find the implementation of object_getClass in objc_class.mm:

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

Ps: The above method definition can be downloaded from the official OpenSource.

As you can see, ultimately this method returns the ISA pointer to the objC that called the method. We just need to know who is ultimately calling the -(Class) Class method in the code in the stem.

Next, we use the clang-rewrite-objc command to convert the problem line into the following code:

NSLog((NSString *)&__NSConstantStringImpl__var_folders_8k_cgm28r0d0bz94xnnrr606rf40000gn_T_Car_3f2069_mi_0, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class"))));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_8k_cgm28r0d0bz94xnnrr606rf40000gn_T_Car_3f2069_mi_1, NSStringFromClass(((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Car"))}, sel_registerName("class"))));
Copy the code

[Father class] [Father class

objc_msgSendSuper(struct objc_super *super, SEL op, ...)
Copy the code

Struct objc_super is defined as follows:

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

By definition, when a method is called using super, whenever the compiler sees the super flag, the current object is called to the superclass method. Essentially, the current object is called and the superclass is called to find the implementation. Super is just a compilation indicator. But the receiver of the message, receiver, is still Self. Finally, when NSObject gets the ISA pointer, it still gets self’s ISA, so we get Son.

To expand: what does the code below output?

@interface Father : NSObject
@end
  
@implementation Father
  
- (Class)class {
    return [Father class];
}

@end

---

@interface Son : Father
@end

@implementation Son

- (id)init {
    self = [super init];
    if (self) {
        NSLog(@"% @", NSStringFromClass([self class]));
        NSLog(@"% @", NSStringFromClass([super class]));
    }
    return self;
}

@end

int main(int argc, const char * argv[]) {
    Son *foo = [[Son alloc]init];
    return0; } -- output: -- Father FatherCopy the code

Question 2: What is the output of the following code?

@interface Sark : NSObject
@end
@implementation Sark
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        
        NSLog(@"% @", [NSObject class]);
        NSLog(@"% @", [Sark class]);
        
        BOOL res1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
        BOOL res2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
        BOOL res3 = [(id)[Sark class] isKindOfClass:[Sark class]];
        BOOL res4 = [(id)[Sark class] isMemberOfClass:[Sark class]];
        NSLog(@"%d--%d--%d--%d", res1, res2, res3, res4);
    }
    return 0;
}
Copy the code

Results: 1-0-0-0

Analysis:

First, let’s take a look at the source of the two methods in the problem stem:

- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}
    
- (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}
Copy the code

It can be learned that:

  • The isKindOfClass is executed by taking its own ISA pointer and comparing it with its own. If the isa pointer does not compare with its own, the isKindOfClass will continue to compare the super class to which the ISA pointer refers. And so on.
  • IsMemberOfClass gets its own ISA pointer and compares it with its own to see if it is equal.
  1. [NSObject Class] after it’s done, I call isKindOfClass, and the first time I check is if the meta class of NSObject and NSObject are equal, and I showed you a very detailed graph when I talked about meta class, We can also see from the diagram that NSObject’s meta Class is not the same as itself. Then the second loop checks whether NSObject is equal to the superclass of the Meta Class. Again, from that diagram, we can see that the superclass of Root class(meta) is Root class(class), which is NSObject itself. So the second loop is equal, so the first line res1 output should be YES.

  2. Isa refers to the Meta Class of NSObject, so it is not equal to the NSObject Class.

  3. [Sark class] isKindOfClass isKindOfClass isKindOfClass isKindOfClass isKindOfClass isKindOfClass isKindOfClass The super Class of the Sark Meta Class refers to the NSObject Meta Class, which is not equal to the Sark Class. The third for loop, the super Class of NSObject Meta Class points to NSObject Class, which is not equal to Sark Class. The fourth loop, the super Class of NSObject Class points to nil, not equal to Sark Class. After the fourth loop, the loop exits, so the third line res3 outputs NO.

  4. Isa refers to Sark’s Meta Class, and Sark’s Class is not the same.