iOS[super class] and [self class]

Keywords: iOS

Recently, the editor's office has recruited iOS development positions. The editor has also given several interview questions to examine the candidate's iOS development level, one of which is as follows:

@implementation Student : Person

- (instancetype)init
{
    self = [super init];
    if (self) {


        id obj1 = [self class];
        id obj2 = [super class];
        NSLog(@"%@",obj1);
        NSLog(@"%@",obj2);

    }
    return self;
}
@end

Most of the candidates answered out Student, [Super class] out Person, only a few candidates answered out Student, of course, as to why the output results are Student, very few can answer.

Next, by converting Student.m into a Cpp file, I'll show you what's going on behind [self class] and [super class].

Transfer Student.m to Student.cpp file through terminal

2 Find Student's init method to analyze code

static instancetype _I_Student_init(Student * self, SEL _cmd) {
 self = ((Student *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Student"))}, sel_registerName("init"));
 if (self) {


  id obj1 = ((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class"));
  id obj2 = ((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Student"))}, sel_registerName("class"));
  NSLog((NSString *)&__NSConstantStringImpl__var_folders_hj_pwgsq9614nb0vq4zd315tcx80000gn_T_Student_e7cbc1_mi_0,obj1);
  NSLog((NSString *)&__NSConstantStringImpl__var_folders_hj_pwgsq9614nb0vq4zd315tcx80000gn_T_Student_e7cbc1_mi_1,obj2);

 }
 return self;
}

So when we call [self class], in fact the compiler eventually converts us to
Objc_msgSend (self, @selector (class). The recipient of the message is the instance object of the current class, and then goes to Student of the class where Self belongs to find the class method.
If there is no class in the current class Student, the class method will be found in the Student parent Person class.
If the Person class does not find the class method, it will eventually find the class method of the top-level parent NSObject.
Finally, the class method of NSObject is found and object_getClass(self) is called, because the message receiver is the current class instance object of self.
So ultimately [self class] outputs Student

So why call super to return [super class] or Student?

From the above code, the final compiler of [super class] is converted to objc_msgSendSuper (struct objc_super*, SEL), where

/// 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 */
};
#endif

objc_super is a structure with a receiver instance inside and a Class super_class pointing to the parent Class of the current class. Through this parent class, the method can be found directly from the parent class. Since the message receiver is also the instance object of the current class, the method of class will eventually be found in the parent NSObject of the Person class if it is not found in the parent class. Find the class method, because NSObject's class method calls object_getClass(self), so the final message receiver is the student instance object, so it returns Student.

Of course, we know what the code looks like after the final compilation of [super class], or we can call the objc_msgSendSuper method manually by ourselves.

- (instancetype)init
{
    self = [super init];
    if (self) {


        id obj1 = [self class];
        id obj2 = [super class];
        NSLog(@"%@",obj1);
        NSLog(@"%@",obj2);

        struct objc_super1 {
            __unsafe_unretained id receiver;
            Class superClass;
        };
        struct objc_super1 obj_super = {self,class_getSuperclass(object_getClass(self))};
        id obj3 = objc_msgSendSuper(&obj_super,@selector(class));

    }
    return self;
}

The output of obj3 is exactly the same as that of direct invocation of [super class]. What if Person overrides the class method?

When we call [super class] in the studentinit method, it first finds the class method in the Person class. When it finds that the Person implements the class method, it calls the [person class] method. At this time, object_getClass(self), which is the instance object of Student, the message receiver, even if it rewrites the Person class. Method, still returns Student, unless an extreme class method implementation returns a nil, so that the final invocation of the [super class] result returns nil

KVO ingeniously uses subclasses to rewrite the class method, making us mistake that [person class] is still the current class Person, rather than the dynamically created subclass NSKVONotifily_Person, which allows the subclass to return to the parent class Class, so calling [person class] to return or Person hides the existence of the NSKVONotifily_Person subclass from the developer.

- (Class)class {
    return class_getSuperclass(object_getClass(self));
}

Posted by MastahUK on Thu, 20 Dec 2018 10:18:04 -0800