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 objectstheisaPointer toClass object. Passes when the object method (- method) is calledinstancetheisafindclassAnd then inclassMethod list to find the corresponding implementation to call.
  • Class objectIsa pointer toMeta - class object. Passes when the class method (+) method is calledclasstheisafindmeta-classAnd finally themeta-classFind 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.

  • instancetheisaPoint to theclass
  • classtheisaPoint to themeta-class
  • meta-classtheisaPoints to the base classmeta-class
  • classthesuperclassPointing to the parent classclass.If there is no parent class,superclassA pointer tonil
  • meta-classthesuperclassPointing to the parent classmeta-class.The base classmeta-classthesuperclassPoints 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

forstudentI don’t knowabcWhere is the method, the only way to know is to go to itClass objectLooking for inside,

  • So let’s go throughisaA pointer to enterStudentOf the classClass objectIf you find one of themabcI’m just going to call it, call it out,
  • If you don’t find it, pass itClass objectthesuperclassA pointer to enterStudentThe parent of class, which is thetaPersonOf 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 isNSObjectOf the classClass objectInside, if you haven’t found it yet, because of itssuperclassfornil, 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, throughStudenttheClass objecttheIsa pointerTo 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, passMeta - class objecttheSuperclass pointerfindStudentThe parent classPersontheMeta - class objectAnd then findabcMethod, find the call, end call trace
  • If not, passPersontheMeta - class objecttheSuperclass pointer, repeat the process in the previous step
  • An analogy, throughMeta - class objecttheSuperclass pointer“And look up one layer at a time
  • If you reach the base class (NSObject)meta-classI haven’t been able to find it yetabcThis is a very special timeSuperclass pointerWill findNSObjectClass objectYou might be wondering, how do we call a class method and get toClass objectThere it is. Save your doubts for now, but just remember that Apple does design it this wayNSObjectClass objectInside, looking forabcIf I do find itabc, will be called
  • If you haven’t found it yet, because at this pointsuperclassisnilFinally, 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 consolepersontheisaInformation. But it seems the system gave only limited informationAnother way to do this is right click in the red boxisaThe 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/xCommand to print the pointer,/Followed by the print parameters,xParameter indicates output in hexadecimal number. Because we knowpersonthisinstanceContains a structure ofisaMember variable,personIt’s a pointer itself, so it passesperson->isaaccessisaThe value of the. In the code,personClassisThe Person classtheClass object, the output shows that,Person isa = 0x001D8001000014D1 personClass = 0x00000001000014d0They both… Not equal!! What’s going on here? I thought we agreed.The instance objectstheisaPoint to theclassThe 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->isaSo let’s print it outisaWhen pointer gets an error message, tell us to saypersonClassThe type ofClassIt’s not a structure. If you don’t understand it, check it out firstClassThe definition,typedef struct objc_class *Class;“And then look downobjc_classThe 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 objecttheIsa pointerafterISA_MASKAfter the transformation, I get the correctMete - class objectThe 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_classInside, 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 *roWhat 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 variablebitsIt is throughbits & FAST_DATA_MASKThat 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?
  1. instanceThe object’sisaPointer toclassobject
  2. classThe object’sisaPointer tometa-classobject
  3. meta-classThe object’sisaPointer to the base class, which is NSObjectmeta-classobject
  • Where is the OC class information stored?
  1. Object methods, attribute information, member variable information, protocol information, storedclassIn the object
  2. Class method, stored inmeta-classIn the object
  3. The specific value of a member variable is stored ininstanceIn 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.