in iOS ~ read.

A interesting inheritance question between self and super

  • Here is a inheritance chain relationship: NSObject -> RootCls -> SubCls
  • Think About a question? What will the followed code to be printed?
@implementation SubCls
- (void)print {
    NSLog(@"%@",[self class]);
    NSLog(@"%@",[self superclass]);
    NSLog(@"%@",[super class]);
    NSLog(@"%@",[super superclass]);
}
@end
  • Ordinary, the wrong answer may look like this:
SubCls  
RootCls  
RootCls  
NSObject  
  • While the truth is:
SubCls  
RootCls  
SubCls  
RootCls  
  • Hard to believe!Then let's look at the c plus plus method which transform from objective-c codes:
static void _I_SubCls_print(SubCls * self, SEL _cmd) {  
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_w4_kn5xrqqn04bcyjv2pkqzv1qr0000gn_T_SubCls_0dc642_mi_0,((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class")));
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_w4_kn5xrqqn04bcyjv2pkqzv1qr0000gn_T_SubCls_0dc642_mi_1,((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("superclass")));
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_w4_kn5xrqqn04bcyjv2pkqzv1qr0000gn_T_SubCls_0dc642_mi_2,((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("SubCls"))}, sel_registerName("class")));
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_w4_kn5xrqqn04bcyjv2pkqzv1qr0000gn_T_SubCls_0dc642_mi_3,((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("SubCls"))}, sel_registerName("superclass")));
}
//The definition of struct `__rw_objc_super`
struct __rw_objc_super {  
    struct objc_object *object; 
    struct objc_object *superClass; 
    __rw_objc_super(struct objc_object *o, struct objc_object *s) : object(o), superClass(s) {} 
};
//The definition of struct `objc_object`
struct objc_object {  
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};
  • As you can see that if we call class and superclass to self, it will call objc_msgSend method. While if we call these method with super it will call objc_msgSendSuper method, so we can have a depth look at this method:
//It has two params, one is a struct with type `objc_super *`, and the other is a `SEL`
objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)  
//We can also see the `objc_super *` struct, it contain a receiver and a Class instance.
struct objc_super {  
    __unsafe_unretained _Nonnull id receiver;
#if !defined(__cplusplus)  &&  !__OBJC2__
    __unsafe_unretained _Nonnull Class class;
#else
    __unsafe_unretained _Nonnull Class super_class;
#endif
};
#endif
  • It's not hard to see that we can see struct __rw_objc_super as objc_super and the final code will be like this:
objc_msgSendSuper((objc_super){self,class_getSuperclass(objc_getClass("SubCls"))},sel_registerName("class"))  
objc_msgSendSuper((objc_super){self,class_getSuperclass(objc_getClass("SubCls"))},sel_registerName("superclass"))  
  • Aditionaly, let's look at the assemble code with objc_msgSendSuper:
/********************************************************************
 * id objc_msgSendSuper(struct objc_super *super, SEL op, ...)
 *
 * struct objc_super {
 *     id receiver;
 *     Class cls;    // the class to search
 * } ********************************************************************/
    ENTRY _objc_msgSendSuper
    MESSENGER_START
    ldr r9, [r0, #CLASS]    // r9 = struct super->class
    CacheLookup NORMAL
    // cache hit, IMP in r12, eq already set for nonstret forwarding
    ldr r0, [r0, #RECEIVER] // load real receiver
    MESSENGER_END_FAST
    bx  r12         // call imp
    CacheLookup2 NORMAL
    // cache miss
    ldr r9, [r0, #CLASS]    // r9 = struct super->class
    ldr r0, [r0, #RECEIVER] // load real receiver
    MESSENGER_END_SLOW
    b   __objc_msgSend_uncached
    END_ENTRY _objc_msgSendSuper
  • Then, let's have a look at the underlying principle in the NSObject.mm which you can download from the apple open source:
+ (Class)class {
    return self;
}
- (Class)class {
    return object_getClass(self);
}
+ (Class)superclass {
    return self->superclass;
}
- (Class)superclass {
    return [self class]->superclass;
}
  • Come to the conclusion, call method class and superclass will make the RootCls receive the class and superClass method, while it don't rewrite the method so it will call the NSObject's method,and it will have the same result as self.
comments powered by Disqus