(Summary of articles on underlying principles of iOS)

(iOS)

The interview questions in this paper mainly involve the interview questions related to ISA placement, inheritance and class structure and the analysis of the interview questions

【 Interview question 】 how many kinds exist?

There is only one copy of a class object because the class information is always in memory

【 baidu interview question 】 The relationship between objc_object and objects

  • All of theobjectAre based onobjc_objectAs a templateinheritanceCome over
  • All objects are fromNSObject(OC), but what really goes to the bottom is oneObjc_object (C/C + +)The structure type of

【 Conclusion 】 The relationship between objc_object and objects is inheritance

What are attribute & member variables & instance variables?

  • attribute(property) : is passed in OCAt sign propertyAnd beltUnderline member variable + setter + getterMethod variables
  • Member variables(IVar) : in the OC class{} is definedandNo underliningThe variables of
  • The instance variables: Available based on the current object typeInstantiated variables, it is a kind ofSpecial member variables, such as NSObject, UILabel, UIButton, etc

What is the difference between a member variable and an instance variable?

  • The instance variables(i.e.Member variablesIn theObject variablesisThe instance variables) : instantiated by an instance object, is aSpecial member variables
  • NSString 是constantType, becauseCannot add propertiesIf the{} defined in a classIs,Member variables
  • Member variablesremoveBasic data type, NSStringEverything elseThe instance variables(i.e.You can add propertiestheMember variables), the instance variables are mainlyDetermine whether it is an object

【 答 案 】 Why are there class methods for class objects in metaclasses?

In exploring in, we learned that instance methods are stored in classes and class methods are stored in metaclasses

In order to explore the phenomenon of our interview questions, the following methods have been defined to explore the attribution of methods

  • Define an instance method and a class method in LGPerson
@interface LGPerson : NSObject - (void)sayHello; + (void)sayHappy; @end @implementation LGPerson - (void)sayHello{ NSLog(@"LGPerson say : Hello!!!" ); } + (void)sayHappy{ NSLog(@"LGPerson say : Happy!!!" ); } @end Copy the codeCopy the code
  • Main the main function used to call custom methods
int main(int argc, const char * argv[]) { @autoreleasepool { LGPerson *person = [LGPerson alloc]; Class pClass = object_getClass(person); lgObjc_copyMethodList(pClass); lgInstanceMethod_classToMetaclass(pClass); lgClassMethod_classToMetaclass(pClass); NSLog(@"Hello, World!" ); } return 0; } copy codeCopy the code
  • LgObjc_copyMethodList: Gets a list of methods for a class
void lgObjc_copyMethodList(Class pClass){ unsigned int count = 0; Method *methods = class_copyMethodList(pClass, &count); for (unsigned int i=0; i < count; i++) { Method const method = methods[i]; NSString *key = NSStringFromSelector(method_getName(method)); LGLog(@"Method, name: %@", key); } free(methods); } copy codeCopy the code
  • The lgInstanceMethod_classToMetaclass function is used to get the instance method of the class
void lgInstanceMethod_classToMetaclass(Class pClass){ const char *className = class_getName(pClass); Class metaClass = objc_getMetaClass(className); Method method1 = class_getInstanceMethod(pClass, @selector(sayHello)); Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello)); Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy)); Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy)); LGLog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4); } copy codeCopy the code
  • The lgClassMethod_classToMetaclass function: The class method used to get the class
void lgClassMethod_classToMetaclass(Class pClass){ const char *className = class_getName(pClass); Class metaClass = objc_getMetaClass(className); Method method1 = class_getClassMethod(pClass, @selector(sayHello)); Method method2 = class_getClassMethod(metaClass, @selector(sayHello)); Method method3 = class_getClassMethod(pClass, @selector(sayHappy)); // Method method4 = class_getClassMethod(metaClass, @selector(sayHappy)); LGLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4); } copy codeCopy the code
  • The lgIMP_classToMetaclass function: used to get the method implementation
void lgIMP_classToMetaclass(Class pClass){ const char *className = class_getName(pClass); Class metaClass = objc_getMetaClass(className); // - (void)sayHello; // + (void)sayHappy; IMP imp1 = class_getMethodImplementation(pClass, @selector(sayHello)); IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayHello)); IMP imp3 = class_getMethodImplementation(pClass, @selector(sayHappy)); IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayHappy)); NSLog(@"%p-%p-%p-%p",imp1,imp2,imp3,imp4); NSLog(@"%s",__func__); } copy codeCopy the code

Here are the printed results of several function calls

So let’s go through the different functions one by one

LgObjc_copyMethodList function analysis

In this function, we’re basically getting a list of the methods in class LGPerson, and as you can see from the instance methods stored in the class, the class methods stored in the metaclass, LGPerson’s method list only prints out the sayHello method

Function analysis of lgInstanceMethod_classToMetaclass

Before analyzing, we need to understandclass_getInstanceMethodThis method is mainly used to obtain the instance method, for this method, Apple has the following descriptionIt basically means:Returns NULL if the specified instance method is not found in the passed class or its parent class

LGPerson (); LGPerson (); LGPerson ();

  • Method1 Address: 0x1000031B0

    • The incomingpClassisLGPerson classYou need to getselName = sayHelloInstance method of
    • First of all inLGPersonIn, there is the frontLGPersonClass knows that it has this instance method, so it returns the instance method found, somethod1The address of the0 x0
  • Method2 address: 0x0

    • The incomingpClassisLGPerson metaclassYou need to getselName = sayHelloInstance method of
    • The search order isMetaclass --> root metaclass --> root class --> nilI never found it until the end, soclass_getInstanceMethodreturnNULL, itsmethod2The address for0x0Is not found
  • Method3 Address: 0x0

    • The incomingpClassisLGPerson classYou need to getselName = sayHappyInstance method of
    • The search order isLGPerson class --> root class --> nil, also not foundsayhelloInstance method, returnNULL, somethod3The address for0x0Is not found
  • Method4 Address: 0x100003148

    • The incomingpClassisLGPerson metaclassYou need to getselName = sayHappyInstance method of
    • First of all inLGPerson metaclassAnd found that there issayHappyInstance method, mainly becauseClass methods of class objects are stored in the metaclass,Class methods are instance methods in a metaclassAnd then return the instance method found, somethod3The address for0x100003148, indicating that the specified instance method is found

The lgClassMethod_classToMetaclass function is analyzed

Before analyzing, we need to understandclass_getClassMethodThis method is mainly used to obtain the class method, for this method, Apple has the following description

Returns NULL if the specified class method is not found in the passed class or its parent class

Class_getClassMethod () ¶ class_getClassMethod (); class_getClassMethod (); class_getMethod (); In the getMeta source code, if CLS is determined to be a metaclass, then the recursive search will not continue, and will directly return this, in order to prevent the metaclass infinite recursive search

Method class_getClassMethod(Class CLS, SEL SEL) {if (! cls || ! sel) return nil; return class_getInstanceMethod(cls->getMeta(), sel); } ⬇️ // NOT identical to this->ISA when this is a metaclass (metaclass) Class getMeta() {if (isMetaClass()) return (Class)this; else return this->ISA(); } copy codeCopy the code

The source code flowchart is shown below

Therefore, the following analysis is made for the print result of this function

  • Method1 address: 0x0

    • pClassLGPerson class.selNamesayHello
    • In the first place to judgeLGPerson classWhether it isThe metaclassAt this time,notTo return toLGPerson metaclassAnd then inThe metaclassLook forsayhelloInstance methods. Search order is as follows:Metaclass --> root metaclass --> root class --> nil, and return NULL
  • Method2 address: 0x0

    • pClassLGPerson metaclass.selNamesayHello
    • In the first place to judgeLGPerson metaclassWhether it isThe metaclassAt this time,is, returns the metaclass directly, and then inThe metaclassLook forsayhelloInstance method, found not found, returnNULL
  • Method3 Address: 0x100003148

    • pClassLGPerson class.selNamesayHappy
    • In the first place to judgeLGPerson classWhether it isThe metaclassAt this time,notTo return toLGPerson metaclassAnd then inThe metaclassLook forsayHappyInstance method, found this instance method, directly return the instance method found
  • Method4 Address: 0x100003148

    • pClassLGPerson metaclass.selNamesayHappy
    • In the first place to judgeLGPerson metaclassWhether it isThe metaclassAt this time,is, returns the metaclass directly, and then inThe metaclassLook forsayHappyInstance method, found this instance method, directly return the instance method found

As a result of the above analysis, we find that method4 is not NULL, so we wonder why there is a sayHappy class method in the metaclass.

The class_getClassMethod method is a metaclass, which is an artificial terminating condition for recursion to prevent infinite recursion

Function analysis of lgIMP_classToMetaclass

class_getMethodImplementationmainlyReturns the concrete implementation of the method, there is the following official explanation for this method

Basically, this function is called when it sends a message to an instance of the class and returns a pointer to the method implementation function. This function is faster than method_getImplementation(class_getInstanceMethod(CLS, name)). The returned function pointer may be a function that points inside runtime, not necessarily to the actual implementation of the method. If the class instance is unable to respond to a selector, the returned function pointer is part of the run-time message forwarding mechanism

We can also verify the above statement by looking at the source code of this method,

IMP class_getMethodImplementation(Class cls, SEL sel) { IMP imp; if (! cls || ! sel) return nil; / / find the method to realize imp = lookUpImpOrNil (nil, sel, CLS, LOOKUP_INITIALIZE | LOOKUP_RESOLVER); // If no message is found, the message is forwarded if (! imp) { return _objc_msgForward; } return imp; } copy codeCopy the code

Next, examine the four prints in this function

  • Imp1 function pointer address: 0x100001d00

    • pClassLGPerson class.selsayHello
    • According to theLGPersonFile, you can find it in the LGPerson classsayHelloSo return the address of an IMP function pointer
  • Imp2 function pointer address: 0x7FFF66238d80

    • pClassLGPerson metaclass.selsayHello
    • According to theClass methods are stored in metaclassesSayHello is an instance method that is not stored in the metaclass, nor does it have any implementationforward
  • Imp3 function pointer address: 0x7FFF66238d80

    • pClassLGPerson class.selsayHappy
    • According to theLGPersonFile,sayHappyIs a class method that is not stored in the class and does not have any implementation of it, so it goesforward
  • Imp4 function pointer address: 0x100001d30

    • pClassLGPerson metaclass.selsayHappy
    • According to theClass methods are stored in metaclassesFile, which can be found in the metaclasssayHappySo return oneAddress of imp function pointer

conclusion

  • class_getInstanceMethod: getInstance methods, if specifiedclassOr itsThe parent class does not containWith a specified selectorInstance methods, it isNULL
  • class_getClassMethod: getClass method, if specifiedclassOr itsThe parent class does not containHaving a specified selectorClass method, it isNULL.
  • class_getMethodImplementation: getmethodstheThe specific implementationIf theDid not find, doforward

【 答 案 】 What is iskindOfClass and isMemberOfClass

There are a few lines of code about iskindOfClass & isMemberOfClass, analyze the final result

  • IskindOfClass & isMemberOfClass class method calls
//----- Use the iskindOfClass & isMemberOfClass method BOOL re1 = [(id)[NSObject class] iskindOfClass :[NSObject class]]; // BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]]; // BOOL re3 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]]; // BOOL re4 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]]; // NSLog(@" re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4); Copy the codeCopy the code
  • IskindOfClass & isMemberOfClass instance method calls
//------iskindOfClass & isMemberOfClass instance method BOOL re5 = [(id)[NSObject alloc] iskindOfClass :[NSObject class]]; // BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]]; // BOOL re7 = [(id)[LGPerson alloc] isKindOfClass:[LGPerson class]]; // BOOL re8 = [(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]]; // NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8); Copy the codeCopy the code

The final results are printed below

The source code parsing

Before analyzing the results, you first need to analyze the source code for both methods

  • IsKindOfClass source code parsing (instance method & class method)
//--isKindOfClass-- class method, object method //+ isKindOfClass: The first comparison is when the metaclass of the obtained class is compared to the passed class, + (BOOL)isKindOfClass:(Class) CLS {// obtain metaclass of Class vs passed Class // Root metaclass vs passed Class // Root Class vs passed Class // example: LGPerson vs metaclass (root metaclass) (NSObject) for (Class TCLS = self->ISA(); tcls; tcls = tcls->superclass) { if (tcls == cls) return YES; } return NO; } / / - isKindOfClass: The first time is to get the object class compared to the passed class, if not equal, (BOOL)isKindOfClass:(Class) CLS {/* get the object's Class vs passed in Class parent vs passed in Class root vs passed in Class nil vs passed in Class */ for (Class tcls = [self class]; tcls; tcls = tcls->superclass) { if (tcls == cls) return YES; } return NO; } copy codeCopy the code
  • IsMemberOfClass Source code Parsing (instance method & class method)
+ (BOOL)isMemberOfClass:(Class) CLS {return self->ISA() == CLS; } //----- instance method //- (BOOL)isMemberOfClass:(Class) CLS {return [self Class] == CLS; } copy codeCopy the code

Source code Analysis summary

  • isKindOfClass

    • Methods:Metaclass (ISA) --> root metaclass (parent) --> root metaclass (parent) --> nil (parent)The incoming classThe contrast of
    • Example method:Object class -> parent -> root -> nilThe incoming classThe contrast of
  • isMemberOfClass

    • Methods:Class of metaclassThe incoming classcontrast
    • Example method:Object's parent classThe incoming classcontrast

Then through the breakpoint debugging, isMemberOfClass class method and instance method process is normal, will go to the above analysis of the source code, and isKindOfClass will not go to the above analysis of the source code (!! Notice here, this is a pit), but instead goes to the source code below, where the class methods and instance methods go to the objc_opt_isKindOfClass method source code

  • The assembly call is as follows

  • objc_opt_isKindOfClassThe method source is as follows
// Calls [obj isKindOfClass] BOOL objc_opt_isKindOfClass(id obj, Class otherClass) { #if __OBJC2__ if (slowpath(! obj)) return NO; Class CLS = obj->getIsa(); // if obj isa Class, then isa isa metaclass; if (fastpath(! CLS ->hasCustomCore()) {for (Class TCLS = CLS; for (Class TCLS = CLS; tcls; tcls = tcls->superclass) { if (tcls == otherClass) return YES; } return NO; } #endif return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass); } copy codeCopy the code

Why is that? This is mainly because it is optimised at compile time in LLVM

So callobjc_opt_isKindOfClassThe actual logic is shown in the figure

Case code execution result analysis

According to the source code analysis, to analyze the result of code execution is 0 or 1

Result parsing using class methods

  • Re1:1, that’s NSObject versus NSObject, plus isKindOfClass

    • NSObject (pass class, i.eThe root class) vs NSObjectA metaclass– not equal
    • NSObject (pass class, i.eThe root class) vs the parent class of the root metaclassThe root class— equal, return 1
  • Re2:0, which is NSObject versus NSObject, uses +isMemberOfClass

    • NSObjectThe root class(passed class) vs NSObject’s metaclass isA metaclass– not equal
  • Re3:0, that’s LGPerson versus LGPerson, using +isisKindOfClass

    • LGPerson (incomingclass) vs LGPerson metaclassThe metaclassLGPerson — not equal
    • LGPerson (incomingclassVs metaclass LGPerson’s parent class isA metaclass– not equal
    • LGPerson (incomingclass) vs the parent class of the root metaclassThe root class– not equal
    • LGPerson (incomingclass) vs the parent of the root classnil– not equal
  • Re4:0, that’s LGPerson versus LGPerson, +isMemberOfClass

    • LGPerson (incomingclass) vs.The metaclass– not equal

Example method result parsing

  • Re5:1 is an NSObject versus an NSObject, using -isKindofclass

    • NSObject (pass class, i.eThe root class) vs objectsisaThe NSObjectThe root classEqual –
  • Re6:1 is an NSObject compared to NSObject, using -isMemberOfClass

    • NSObject (the passed class, the root class) vs the class of the object, the NSObject root class — equal
  • Re7:1 is a comparison of LGPerson objects to LGPerson, using -isKindofClass

    • LGPerson (pass class) vs objectisaSo LGPerson — equal
  • Re8:1 is a comparison of LGPerson objects to LGPerson, using -isMemberOfClass

    • LGPerson (passed in class) vs the class of the object which is LGPerson — equal