preface

The source environment involved in this article is ObjC4-818.2

We’ve used self and super countless times in our initialization methods, so we’re going to look at them today.

  • Create a classFatherInherited fromNSObject
  • Create a classSonInherited fromFather
@implementation Son -(instancetype)init{ if (self = [super init]) { NSLog(@"1 -- %@",[self class]); NSLog(@"2 -- %@",[super class]); } return self; } @end outputs 1 -- Son 2 -- SonCopy the code
  • 1Print out the currentSonNo problem, then why2No outputSonThe parent classFather????
  • why[super init]Return a subclass instance object instead of a superclass instance object.

self

See the implementation of the class method in the source environment’s nsobject.mm

- (Class)class { return object_getClass(self); } Class object_getClass(id obj) { if (obj) return obj->getIsa(); else return Nil; } Inline Class objc_object::getIsa() { isTaggedPointer())) return ISA(); . } inline Class objc_object::ISA(bool authenticated) { ASSERT(! isTaggedPointer()); return isa.getDecodedClass(authenticated); } inline Class isa_t::getDecodedClass(bool authenticated) { ... return getClass(authenticated); } inline Class isa_t::getClass(MAYBE_UNUSED_AUTHENTICATED_PARAM bool authenticated) { ... uintptr_t clsbits = bits; . clsbits &= ISA_MASK; . return (Class)clsbits; }Copy the code

The isa pointer to a nonpointer object contains a pointer to a class object.

We might be curious about the class method why isn’t self the object that he calls NSObject?

- (Class) Class {// who is self accessed here?? return object_getClass(self); }Copy the code

We know that the function is executed at the bottom of OC via objc_msgSend, which has two default arguments self and _cmd

  • selfFor message receiver
  • _cmdFor the function name
  • .And then parameters
id objc_msgSend(id self, SEL _cmd, ...) ;Copy the code

There are also default arguments self and _cmd in the function, which correspond to the default arguments in objc_msgSend and can be easily understood as

- (Class)class(id self, SEL _cmd) {
    ......
}
Copy the code

Because we don’t implement class methods in either Father or Son custom classes, the message lookup process looks up the inheritance chain to the class method in NSObject, but the self object is still an instance object of Son.

Let’s do one more little example of implementing the method fatherMethod in Father

@interface Father : NSObject
- (void)fatherMethod;
@end

@implementation Father
- (void)fatherMethod{
    NSLog(@"self == %@",self);
}
@end
Copy the code

There are no methods implemented in subclass Son, so we call the fatherMethod method with the instance object of the subclass

int main(int argc, char * argv[]) {

@autoreleasepool {
        Son *son = [[Son alloc] init];
        [son fatherMethod];
}
    return 0;
}
Copy the code

The execution result is

self == <Son: 0x28211c140>
Copy the code

No matter where the method is implemented, the self accessed in the method is the message receiver.

super

Self is the message receiver and is one of the default arguments to the message sending function objc_msgSend. Super is not so simple; it is not a parameter but a compiler keyword. We implement it in son.m

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

We explore the underlying principles through clang and assembly

throughclangLook at the underlying implementation

The terminal goes to the son. m path and runs the command

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc Son.m
Copy the code

You can see that the underlying implementation of [super class] in son.cpp is

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) {} }; . ((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Son"))}, sel_registerName("class"))Copy the code

The message is sent through objc_msgSendSuper instead of objc_msgSend, and the message receiver becomes the __rw_objc_super structure pointer

Viewing by assembly

The difference here is that it’s executedobjc_msgSendSuper2

The source code to explore

We enter the lower world with these questions

objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)

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

The arguments to both functions are the same, with two default arguments

  • objc_superThe type ofsuper
  • SELThe type ofop
  • The parameter

Objc_super structure is (objC2 environment)

struct objc_super {
    __unsafe_unretained _Nonnull id receiver;
    __unsafe_unretained _Nonnull Class super_class;
    /* super_class is the first class to search */
};
Copy the code

Note that super_class is the first class to be searched when a method is searched

Search the objc_msgSendSuper and objc_msgSendSuper2 implementations in the objC-msG-arm64.s file

ENTRY _objc_msgSendSuper // Go to L_objc_msgSendSuper2_body and continue b L_objc_msgSendSuper2_body END_ENTRY _objc_msgSendSuper ENTRY _objc_msgSendSuper2 UNWIND _objc_msgSendSuper2, NoFrame // Unlike objc_msgSend, P16 does not point to class but to superclass LDP P0, p16, [x0] ldr p16, [x16, #SUPERCLASS] L_objc_msgSendSuper2_body: CacheLookup NORMAL, _objc_msgSendSuper2, __objc_msgSend_uncached END_ENTRY _objc_msgSendSuper2Copy the code

Unlike objc_msgSend, p16 does not point to class but to superclass, but the message receiver, receiver, is the same. Verify this.

Define and implement methods fatherMethod and method in the Father class

@interface Father : NSObject
- (void)fatherMethod;
- (void)method;
@end

@implementation Father
- (void)fatherMethod{
    NSLog(@"self == %@",self);
}

- (void)method{
    NSLog(@"father-method");
}
@end
Copy the code

Override the fatherMethod method in subclass Son and implement the method method

@interface Son : Father
- (void)method;
@end

@implementation Son
- (void)fatherMethod{
    [super fatherMethod];
    [super method];
}

- (void)method{
    NSLog(@"son-method");
}
@end
Copy the code

run

int main(int argc, char * argv[]) { @autoreleasepool { Son *son = [[Son alloc] init]; [son fatherMethod]; } return 0; } self == <Son: 0x2806b8130> father-methodCopy the code

The message receiver in [super fatherMethod] is still an instance object of subclass Son, so self accessed from fatherMethod is an instance object of Son

Although subclass Son also implements method methods, [super method] is found from Father

summary

Self calls method [self XXX] and super calls method [super XXX]

  • The similarities are as follows: Message receiverreceiverIs the same
  • The differences are:[super xxx]The message lookup process starts with the parent class
-(instancetype)init{
    if (self  = [super init]) {
        ......
    }
    return self;
}
Copy the code
+ (id)init {
    return (id)self;
}
Copy the code

[super init] looks for the init function from the parent class, but the message receiver is still a subclass instance object, so self called and returned in init is the subclass instance object.

Refer to the article

Self and super in # OC