Welcome to the iOS Exploration series.

  • IOS explores the alloc process
  • IOS explores memory alignment &malloc source code
  • IOS explores ISA initialization & pointing analysis
  • IOS exploration class structure analysis
  • IOS explores cache_T analysis
  • IOS explores the nature of methods and the method finding process
  • IOS explores dynamic method resolution and message forwarding mechanisms
  • IOS explores the dyLD loading process briefly
  • The loading process of iOS explore classes
  • IOS explores the loading process of classification and class extension
  • IOS explores isa interview question analysis
  • IOS Explore Runtime interview question analysis
  • IOS explores KVC principles and customization
  • IOS explores KVO principles and customizations
  • IOS explores the principle of multithreading
  • IOS explores multi-threaded GCD applications
  • IOS explores multithreaded GCD underlying analysis
  • IOS explores NSOperation for multithreading
  • IOS explores multi-threaded interview question analysis
  • IOS Explore the locks in iOS
  • IOS explores the full range of blocks to read

Writing in the front

The interview questions covered in this article are:

  • isKindOfClassandisMemberOfClassThe difference between
  • [self class]and[super class]The difference between
  • Isa integrated use – memory offset

IsKindOfClass and isMemberOfClass

This is an interview question involving isa bitmap, dare to guess the result

#import <Foundation/Foundation.h> #import "FXPerson.h" #import <objc/runtime.h> int main(int argc, const char * argv[]) { @autoreleasepool { BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]]; //1 BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]]; // 0 BOOL re3 = [(id)[FXPerson class] isKindOfClass:[FXPerson class]]; //0 BOOL re4 = [(id)[FXPerson class] isMemberOfClass:[FXPerson class]]; // 0 NSLog(@"\n re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4); BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]]; //1 BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]]; // 1 BOOL re7 = [(id)[FXPerson alloc] isKindOfClass:[FXPerson class]]; //1 BOOL re8 = [(id)[FXPerson alloc] isMemberOfClass:[FXPerson class]]; // 1 NSLog(@"\n re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8); } return 0; }Copy the code

Let’s explore the implementation of isKindOfClass and isMemberOfClass without revealing the answer

+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}
Copy the code
  • This is a similar tofor (int i = 0; i < 3; i ++)The for loop
    • object_getClassGets the class of the current class object —The metaclassInitialization,tcls
    • As long astclsWe can continue the loop if we have a valuetclsfornilTo end the for loop
    • achievetclsClass, as its new value, continues the next loop
  • When there’s one in the for looptcls == clsTo return toYES
  • Completes the for loop and returns without satisfying the conditionNO

Conclusion 1: +isKindOfClass is a metaclass and its superclass vs class

+ (BOOL)isMemberOfClass:(Class)cls {
    return object_getClass((id)self) == cls;
}
Copy the code
  • object_getClassGets the class of the current class object —The metaclass, and the class itselfclsTo compare the
  • In contrast to+isKindOfClassThere’s no superclass comparison, so+isMemberOfClassIs available for YES+isKindOfClassTo YES

+isMemberOfClass is a metaclass vs class

Isa bitmap

  • NSObject metaclasswithNSObject classNot equal,NSObject Is the parent of the metaclass(pointing toNSObject class) andNSObject classEqual – YES
  • NSObject metaclasswithNSObject classNot equal – NO
  • FXPerson metaclasswithFXPerson classNot equal,The parent of the FXPerson metaclasswithFXPerson classNot equal – NO
  • FXPerson metaclasswithFXPerson classNot equal – NO

Instead, the instance objects call -isKindofClass and -isMemberofClass

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

- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}
Copy the code

-isMemberofClass compares the instance object’s class (the current class) with CLS, -isKindofClass adds one more step to the parent of the for loop class object

-iskindofClass is the class itself and its superclass vs class

-isMemberofClass is a class vs class

The following four results are analyzed as follows:

  • NSObject classwithNSObject classEqual – YES
  • NSObject classwithNSObject classEqual – YES
  • FXPerson classwithFXPerson classEqual – YES
  • FXPerson classwithFXPerson classEqual – YES

[self class] [super class]

FXSon inherits from FXFather, the main program initializes FXSon, asking for print content and ideas

#import "FXSon.h"

@implementation FXSon

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

@end
Copy the code

The print result is as follows

[self class]
NSObject.mm

  • classMethod return class
  • superclassMethod returns the parent class of the class
+ (Class)class {
    return self;
}

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

+ (Class)superclass {
    return self->superclass;
}

- (Class)superclass {
    return [self class]->superclass;
}
Copy the code

This code explains [self class] and [self superclass], but what about the other two?

The terminal clang compiles the code to super.cpp, and you can see the initialization of the underlying code

clang -rewrite-objc FXSon.m -o super.cpp
Copy the code

objc_msgSend
objc_msgSendSuper

See the definition of objc_msgSendSuper in the source code. Objc_super is mentioned in the comments

objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... * / )
    OBJC_AVAILABLE(10.0.2.0.9.0.1.0.2.0);
    
/// Specifies the superclass of an instance. 
struct objc_msgSendSuper {
    /// 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

From the above source, messages are sent to objc_super using objc_msgSendSuper, which has two elements under ObjC2.0 — receiver of type ID and super_class of type Class

In fact, this method has been mentioned in the iOS exploration method nature and message lookup process, and then I did a parody


[super class]

So why did Apple do this? Put the message lookup and ISA bitmap together!

Son instance object
Instance methods
FXSon class

  • call[self class]That’s son lightFXSon->FXFather->NSObjectAsk dad in order- class method
  • call[super class]Son skipFXSonGo straight throughFXFather->NSObjectTo find the
  • There are more than[super class]Find out how to write the class method faster
    • In the structure[self class]Instead of[super class]Find it directlyNSObjct

Note: When [self class] is changed to [FXFather class] in the structure, because the class method is in the metaclass, the FXFather metaclass ->NSObject metaclass ->NSObject root metaclass will look for the +class method and output FXSon as well

Conclusion:

  • [self class]Is to send a messageobjc_msgSendThe message receiver isself, the method number isclass
  • [super class]Is to send a messageobjc_msgSendSuperThe message receiver isself, the method number isclass, onlyobjc_msgSendSuperWill skipselfThe search for

Memory offset

This is one of the classic “crazy” memory offset interview questions, if you haven’t studied it, chances are it’s hard to answer

1. The original problem

Does the program run? Is the output normal?

#import "ViewController.h" @interface FXPerson : NSObject @property (nonatomic, copy) NSString *name; @end@implementation FXPerson - (void)printMyProperty {NSLog(@" currently printcontent is %s", __func__); } / / @ end -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- / / @ implementation ViewController - (void) viewDidLoad {[super viewDidLoad]; id cls = [FXPerson class]; void *obj= &cls; [(__bridge id)obj printMyProperty]; FXPerson *p = [FXPerson new]; [p printMyProperty]; } @endCopy the code

The result is exactly the same as the normal initialization object, but it is not possible to say yes or no during the interview, but also to say why

Normal initialization: pointer P -> instance object ISA -> class object

  • The nature of the object isobjc_object, the first element isisa
  • Pointer to the pstoreFXPerson classInstance out of the object memory address, soPointer to the pThe first address to the object —P -> Instance object ISA
  • Instance objectsIsa pointingClass objectInstance object ISA -> class object

Pointer obj-> pointer CLS -> class object

  • id cls = [LGPerson class]Gets a pointer to the class object
  • void *obj= &clsGets the object obj that points to CLS

2. Develop a

Modify the print method printMyProperty – prints not only the method, but also the property name

- (void) printMyProperty {NSLog (@ - - - "the printing content for % s % @", __func__, self, name); }Copy the code

Rerun the code and get the following result

The current print is -[FXPersonprintMyProperty] -- -- -- < ViewController: 0 x7fc72cd09450> The current print is -[FXPersonprintMyProperty] -- -- -- (null)Copy the code

Why is the memory address of the ViewController printed when the property name is not assigned?

  • Because of the stackAfter the first in and out the other.viewDidLoadPush stretching stack space first, then in turnThe self, _cmdA local variable
  • call[super viewDidLoad], continue to addSuper_class, self
  • In normal case, get name, essentially p memory address offset 8 bytes down
  • The same operation is obj memory address offset by 8 bytesself

3. 2

Modify viewDidLoad — add a temporary string variable before obj

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSString *temp = @"1";
    id cls = [FXPerson class];
    void *obj= &cls;
    [(__bridge id)obj printMyProperty];
    
    FXPerson *p = [FXPerson alloc];
    [p printMyProperty];
}
Copy the code

Rerun the code and get the following result

The current print is -[FXPersonprintMyProperty]——————1 The current print is -[FXPersonprintMyProperty] -- -- -- (null)Copy the code

Similarly, if the temp variable is already available before obj is pushed, calling self.name will call temp

4. 3

Remove temporary variables, add the string property Hobby to the FXPerson class, and print hobby instead. Run

ViewController
obj
super_class

5. 4

① Remove [super viewDidLoad] and run

② Change the type of temporary variable to NSInteger

These two cases are called wild Pointers — the offset of the pointer is not correct, and the starting address of the corresponding variable is not obtained

6. All change is inseparable from the theory of its ancestor

int a = 1;
int b = 2;
int c = 3;
int d = 4;
NSLog(@"\na = %p\nb = %p\nc = %p\nd = %p\n",&a,&b,&c,&d);
Copy the code

Print the result

a = 0x7ffee0ebd1bc
b = 0x7ffee0ebd1b8
c = 0x7ffee0ebd1b4
d = 0x7ffee0ebd1b0
Copy the code

Local variables are stored in the order defined, starting from the bottom of the stack (high address), one by one

The picture of the Runtime in the interview question is very vivid

Write in the back

Interview questions are a way for the interviewer to play with your knowledge and show your proficiency. Only in the usual practice and research, in order to leave a good impression on the interviewer