The nature of OC objects (top) : The underlying implementation principle of OC objects
OC Object nature (Middle) : The type of OC object
Isa pointer
First sum up the problem we have analyzed in the classification of objects, OC object atmosphere three types
Instance object, which contains
- Member variables
- Special member variable isa pointer
The class object, used to describe instance objects, contains
- Isa pointer
- Superclass pointer
- Attribute information
- Object method information (- method)
- Protocol information
- Description of a member variable of an instance object
The meta-class object, used to store class methods, contains
- Isa pointer
- Superclass pointer
- Class method information (+ method)
For a class, there must be some relationship between its instance, class, and Mete-class objects. Let’s say that connection doesn’t exist, and let’s see what happens. Let’s say I call a method on an instance object
[InstanceObj InstanceObjMethod];
Copy the code
The bottom of it is
objc_msgSend(instanceObj, @sel_registerName("instanceObjMethod"));
Copy the code
That is, sending a message to the instanceObj object.
If an instance object is not associated with the outside world and only has some member variables inside it, it is impossible to make a method call because the object method is stored in the class object. The same is true for calling the + method on the class object. To complete the call to the + method. So the ISA pointer is just an association between them. You can see the following figure to understand what ISA does.
It can be roughly summed up as
The instance objects
theisa
Pointer toClass object
. Passes when the object method (- method) is calledinstance
theisa
findclass
And then inclass
Method list to find the corresponding implementation to call.Class object
Isa pointer toMeta - class object
. Passes when the class method (+) method is calledclass
theisa
findmeta-class
And finally themeta-class
Find the corresponding implementation to call.
With isa bridging, I should be able to understand the OC message sending and method invocation process one step closer.
Superclass pointer
Superclass = superclass = superclass Suppose we have the following classes
@interface Person : NSObject
@end
@interface Student : Person
@end
Copy the code
We know that superclass Pointers exist in class objects and meta-class objects. Let’s illustrate it according to the following illustration:
A superclass pointer in a class’s class object points to the superclass of that class’s parent class. When a Student instance calls a Person method, it finds Student’s class through ISA. It then finds the class object of Person (Student’s parent) through the superclass of the class object, and finally finds the implementation of the corresponding object method (- method) to call
A superclass pointer in a meta-class refers to a meta-class object of the superclass of that class. When Student’s class calls Person’s class method, isa finds Student’s meta-class. The superclass of this meta-class object is used to find the meta-class object of Person (Student’s parent), and finally find the implementation of the corresponding class method (+ method) to call
Isa, superclass summary
In the figure above, we use Student instead of subclass, Person instead of superclass, and NSObject instead of RootClass to make it easier to understand.
instance
theisa
Point to theclass
class
theisa
Point to themeta-class
meta-class
theisa
Points to the base classmeta-class
class
thesuperclass
Pointing to the parent classclass
.If there is no parent class,superclass
A pointer tonil
meta-class
thesuperclass
Pointing to the parent classmeta-class
.The base classmeta-class
thesuperclass
Points to the base classclass
Instance calls the object method’s track we start with [student ABC]; For example, student is an instance object of the student class, and the call trajectory is shown below
forstudent
I don’t knowabc
Where is the method, the only way to know is to go to itClass object
Looking for inside,
- So let’s go through
isa
A pointer to enterStudent
Of the classClass object
If you find one of themabc
I’m just going to call it, call it out,- If you don’t find it, pass it
Class object
thesuperclass
A pointer to enterStudent
The parent of class, which is thetaPerson
Of the classClass object
, repeat the search logic of the previous step- And so on, level by level, and if you end up in the base class, which is
NSObject
Of the classClass object
Inside, if you haven’t found it yet, because of itssuperclass
fornil
, you will eventually encounter a classic error[ERROR: unrecognized selector sent to instance]
, the call trace ends
Class calls the trace of a class methodWe take the
[Student abc];
For example, the call trajectory is shown as follows
For the Student class, we don’t know where the ABC is either. We know that class methods are specified to be placed in a meta-class object, so
- First of all, through
Student
theClass object
theIsa pointer
To find theMeta - class object
“, and then look in the list of methods to see if there are anyabc
, if there is, call, call logic ends.- If not, pass
Meta - class object
theSuperclass pointer
findStudent
The parent classPerson
theMeta - class object
And then findabc
Method, find the call, end call trace- If not, pass
Person
theMeta - class object
theSuperclass pointer
, repeat the process in the previous step- An analogy, through
Meta - class object
theSuperclass pointer
“And look up one layer at a time- If you reach the base class (
NSObject
)meta-class
I haven’t been able to find it yetabc
This is a very special timeSuperclass pointer
Will findNSObjec
tClass object
You might be wondering, how do we call a class method and get toClass object
There it is. Save your doubts for now, but just remember that Apple does design it this wayNSObjec
tClass object
Inside, looking forabc
If I do find itabc
, will be called- If you haven’t found it yet, because at this point
superclass
isnil
Finally, the system will report an error
Interview question where does the ISA pointer point?
According to the above sorting and summary, we can draw a conclusion
isa(of instance) –> isa(of class) –> isa(of meta-class)
Let’s verify this in code
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface CLPerson : NSObject <NSCopying>
@end
@implementation CLPerson
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
CLPerson *person = [[CLPerson alloc] init];
Class personClass = [CLPerson class];
Class personMetaClass = object_getClass(personClass);
NSLog(@"%p %p %p", person, personClass, personMetaClass);
}
return 0;
}
Copy the code
Let’s put a breakpoint in the code and look at it from the consoleperson
theisa
Information. But it seems the system gave only limited informationAnother way to do this is right click in the red boxisa
The first drop-down menu has a print functionPrint Description of "xxx"
, you can get more detailed outputIt looks like the results are still wrapped up in the system and if you’re used to working on code quickly, you can do thatBut I still like to use LLDB to view, compare, and copy data.throughp/x
Command to print the pointer,/
Followed by the print parameters,x
Parameter indicates output in hexadecimal number. Because we knowperson
thisinstance
Contains a structure ofisa
Member variable,person
It’s a pointer itself, so it passesperson->isa
accessisa
The value of the. In the code,personClass
isThe Person class
theClass object
, the output shows that,Person isa = 0x001D8001000014D1
personClass = 0x00000001000014d0
They both… Not equal!! What’s going on here? I thought we agreed.The instance objects
theisa
Point to theclass
The object?
Person ->isa == personClass; person->isa == personClass; isa == personClass; The system provides us with an ISA_MASK, which can be found in the objC4 source code. I’ll just post it
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t deallocating : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 19
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# define ISA_MAGIC_MASK 0x001f800000000001ULL
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t deallocating : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 8
# define RC_ONE (1ULL<<56)
# define RC_HALF (1ULL<<7)
# else
# error unknown architecture for packed isa
# endif
Copy the code
Please see here is divided into ARM64 and X86_64, respectively corresponding to mobile device development and MAC development. My code is a MAC command line project, so let’s try it out with this value for x86And as you can see, the result is pretty obvious. We get the address of the personClass by performing an & with ISA_MASK. Similarly, let’s try the ISA pointer for personClass.And I tried to passpersonClass->isa
So let’s print it outisa
When pointer gets an error message, tell us to saypersonClass
The type ofClass
It’s not a structure. If you don’t understand it, check it out firstClass
The definition,typedef struct objc_class *Class;
“And then look downobjc_class
The details of the
struct objc_class { Class _Nonnull isa OBJC_ISA_AVAILABILITY; #if ! __OBJC2__ Class _Nullable super_class OBJC2_UNAVAILABLE; const char * _Nonnull name OBJC2_UNAVAILABLE; long version OBJC2_UNAVAILABLE; long info OBJC2_UNAVAILABLE; long instance_size OBJC2_UNAVAILABLE; struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE; struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE; struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE; struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE; #endif } OBJC2_UNAVAILABLE;Copy the code
Although there is an ISA pointer inside this structure, the OBJC2_UNAVAILABLE at the end; Remind us that this is an outdated API. However, we concluded in the first article that, knowing that the first member variable in a class object is indeed an ISA pointer, we can handle this problem with a trick
struct cl_objc_class {
Class isa;
};
int main(int argc, const char * argv[]) {
@autoreleasepool {
CLPerson *person = [[CLPerson alloc] init];
Class personClass = [CLPerson class];
struct cl_objc_class *personClass2 = (__bridge struct cl_objc_class *)(personClass);
Class personMetaClass = object_getClass(personClass);
NSLog(@"%p %p %p", person, personClass, personMetaClass);
}
return 0;
}
Copy the code
We define a struct that contains an ISA pointer, and then use that struct type to read the contents of the personClass. We use personClass2 to try this againOk, and it says,Class object
theIsa pointer
afterISA_MASK
After the transformation, I get the correctMete - class object
The address. To this point, the above interview question I believe you can be answered completely. Let me summarize this with a graph
You might also ask, what about the superclass pointer, does it also require a mask conversion? The answer is no and can be verified using the same method as above, which will not be described here. The isa pointer isa little bit special anyway, just remember the details about ISA_MASK.
A deep look at the inner structure of class/meta-class —-struct objc_class
In the nature of OC object (1), we learn a fact that in the class object, store a class method list, attribute information, protocol information, member variable information; In the meta-class object, the class information of the class is stored. But it has not been carefully tested. Let’s take a look at that. Struct objc_class*, struct objc_class*, struct objc_class*, struct objc_class* The above paragraph we have seen its structure, unfortunately it is a deprecated API, so we must go to the latest source code, to see its implementation. In the objC4 source code, we find the following objC_class implementation
struct objc_class : objc_object { // Class ISA; Class superclass; cache_t cache; // formerly cache pointer and vtable class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags class_rw_t *data() { return bits.data(); } // Here are a bunch of methods... . . }Copy the code
Objc_class is a C++ struct. If you are not familiar with C++, you can use the OC class. They are very similar to each other. Objc_class is derived from objc_Object. We can see the implementation of objc_Object in objc-private.h
struct objc_object { private: isa_t isa; // The rest are methods... . . }Copy the code
As you can see, it’s actually an ISA pointer. So, when combined with the contents of objc_class, we can interpret this structure as follows
struct objc_class { isa_t isa; Class superclass; cache_t cache; // formerly cache pointer and vtable class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags class_rw_t *data() { return bits.data(); } // Here are a bunch of methods... . . }Copy the code
Obviously,objc_class
Inside, the first two members are ISA and Superclass, can’t escape. But the following does not seem to be what we expected, there is no method list, attribute protocol information, etc. But we can see the first method here, which returns oneclass_rw_t *
Class stands for class, Rw usually stands for readwrite, and T usually refers to table, which is the information that a class can read or write. Then we have reason to suspect there must be something in here. Go in and have a lookSure enough, the original methods, properties, and protocol information are all there. At the same time, we found anotherclass_ro_t *ro
What information in a class object is read-only? Yeah, member variable information, so let’s go in and checkIt looks like the inference is right, the member variable information is actually found. Ivars is the name and type of the member variable, so it is stored in the class object. The value of the member variable is stored in the specific instance object.
A meta-class is structurally the same as a class, except that some things may not be needed, such as attributes and protocol lists. The meta class method information is actually in the list of methods that we just saw, yes. The class object’s object method information is also placed in this method list
And the other thing is, how do I put it, look at the picture
On the way we saw,objc_class
We have a member variablebits
It is throughbits & FAST_DATA_MASK
That will beobjc_class
It’s associated with its read-write table. Here I quote a PPT of Da Shen to sum upstruct objc_class
The structure of the
Interview Answers
- Where does the isa pointer to the object point?
instance
The object’sisa
Pointer toclass
objectclass
The object’sisa
Pointer tometa-class
objectmeta-class
The object’sisa
Pointer to the base class, which is NSObjectmeta-class
object
- Where is the OC class information stored?
- Object methods, attribute information, member variable information, protocol information, stored
class
In the object- Class method, stored in
meta-class
In the object- The specific value of a member variable is stored in
instance
In the object
The nature of OC objects (top) : The underlying implementation principle of OC objects
OC Object nature (Middle) : The type of OC object
Special note
This series of articles are summarized from the basic principles of OC taught by MJ in Tencent class. The relevant pictures are taken from the courseware in the course.