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:
isKindOfClass
andisMemberOfClass
The 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 to
for (int i = 0; i < 3; i ++)
The for loopobject_getClass
Gets the class of the current class object —The metaclass
Initialization,tcls
- As long as
tcls
We can continue the loop if we have a valuetcls
fornil
To end the for loop - achieve
tcls
Class, as its new value, continues the next loop
- When there’s one in the for loop
tcls == cls
To return toYES
- Completes the for loop and returns without satisfying the condition
NO
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_getClass
Gets the class of the current class object —The metaclass
, and the class itselfcls
To compare the- In contrast to
+isKindOfClass
There’s no superclass comparison, so+isMemberOfClass
Is available for YES+isKindOfClass
To YES
+isMemberOfClass is a metaclass vs class
Isa bitmap
NSObject metaclass
withNSObject class
Not equal,NSObject Is the parent of the metaclass
(pointing toNSObject class
) andNSObject class
Equal – YESNSObject metaclass
withNSObject class
Not equal – NOFXPerson metaclass
withFXPerson class
Not equal,The parent of the FXPerson metaclass
withFXPerson class
Not equal – NOFXPerson metaclass
withFXPerson class
Not 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 class
withNSObject class
Equal – YESNSObject class
withNSObject class
Equal – YESFXPerson class
withFXPerson class
Equal – YESFXPerson class
withFXPerson class
Equal – 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
class
Method return classsuperclass
Method 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->NSObject
Ask dad in order- class method
- call
[super class]
Son skipFXSon
Go straight throughFXFather->NSObject
To 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
- In the structure
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_msgSend
The message receiver isself
, the method number isclass
[super class]
Is to send a messageobjc_msgSendSuper
The message receiver isself
, the method number isclass
, onlyobjc_msgSendSuper
Will skipself
The 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 is
objc_object
, the first element isisa
Pointer to the p
storeFXPerson class
Instance out of the object memory address, soPointer to the p
The first address to the object —P -> Instance object ISA
Instance objects
Isa pointingClass object
–Instance object ISA -> class object
Pointer obj-> pointer CLS -> class object
id cls = [LGPerson class]
Gets a pointer to the class objectvoid *obj= &cls
Gets 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 stack
After the first in and out the other
.viewDidLoad
Push stretching stack space first, then in turnThe self, _cmd
A 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 bytes
self
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