iOS-isa pointer; objc instance object, class, metaclass, root class, root metaclass

1. Object (objc instance object), Class (class), Metaclass (metaclass), Rootclass (root class), Rootclass's metaclass (root metaclass)

To understand the isa pointer in iOS, we cannot do without several data structures of classes in Objective-C; in the type structure of Objective-C, Object (instance), Class (class), Metaclass (metaclass), Rootclass (root class), Rootclass's metaclass (root metaclass), and these are objects.

Take a look at the Objective-C object model diagram to help understand:

Objective-C---->C/C++----->assembly language---->machine language; we know that Objective-C is an object-oriented language, and all its objects are instantiated by their corresponding classes In fact, the class itself is also an object;

In Objective-C, almost all classes we use are subclasses of the NSObject class 

We open the NSObject.h file in the #import <objc/runtime.h> header file in Xcode or Apple's open source code

(Apple's official open source objc4 source code online browsing ; objc4 source code download , the objc4-818.2 version used in this article)

The NSObject class definition format is as follows (ignoring its method declaration):

@interface NSObject <NSObject> {
    Class isa;
}

Open the objc.h file and you will find that the class (CLass) and instance object (objc, or instance) are defined as follows:

/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class; //class类对象

/// Represents an instance of a class.
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

/// A pointer to an instance of a class.
typedef struct objc_object *id; //objc实例对象

Class is a pointer to the objc_class (class) structure, and id is a pointer to the objc_object (instance object) structure.

The class structure pointed to by the isa pointer in objc_object (instance object) is called objc_class (the class of the object), which stores ordinary member variables and object methods (methods beginning with "-") .

The class structure pointed to by the isa pointer in objc_class (class) is called metaclass (metaclass of the class), which stores member variables of static type and methods of static type (methods beginning with "+") .

/// Represents an instance of a class.
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

The objc_object (instance object) structure has only one member attribute of isa, which points to objc_class (the class of the object).

in the runtime.h file

struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;   //isa指针,指向metaclass(该类的元类)

#if !__OBJC2__

    Class super_class   //指向objc_class(该类)的super_class(父类)

    const char *name    //objc_class(该类)的类名

    long version        //objc_class(该类)的版本信息,初始化为0,可以通过runtime函数class_setVersion和class_getVersion进行修改和读取

    long info           //一些标识信息,如CLS_CLASS表示objc_class(该类)为普通类。ClS_CLASS表示objc_class(该类)为metaclass(元类)

    long instance_size  //objc_class(该类)的实例变量的大小

    struct objc_ivar_list *ivars    //用于存储每个成员变量的地址

    struct objc_method_list **methodLists   //方法列表,与info标识关联

    struct objc_cache *cache        //指向最近使用的方法的指针,用于提升效率

    struct objc_protocol_list *protocols    //存储objc_class(该类)的一些协议
#endif

} OBJC2_UNAVAILABLE;

objc_class (class) has many more members than objc_object (instance object) structure, and the function of each member is introduced in the note.

Therefore, we will find objc instance objects, class objects, metaclasses, root classes, root metaclasses

1. Instance object (Object): An object or instance objc we created is actually a struct objc_object structure, which has only one member variable, which is a variable isa of Class type, and is also a structure pointer. This objc object The isa pointer points to his class object (that is, the class we usually call)

The layout structure of objc objects in memory is as follows:

Structural diagram of an Objective-C object
ISA pointer
Instance variables of the root class
Instance variables of the penultimate parent class
...
Instance variables of the parent class
class instance variables

Class (CLass): stores related data of Object instances, such as: instance method list, member variable list, attribute list.

Metaclass (Metaclass): store Class-related data, such as: class method list, class information, etc.

The location of isa and its function_the first picture

二、isa指针

Let's look at the source code of the NSObject.mm file again, and look at the instance method - (CLass)class

// 类方法,返回自身
+ (Class)class {
    return self;
}
 
// 实例方法,查找isa(类)
- (Class)class {
    return object_getClass(self);
}

We create an instance object objc, call - (CLass) class, actually call the object_getClass method

Let's click the object_getClass method to see the source code definition, in the objc-class.mm file

/***********************************************************************
* object_getClass.
* Locking: None. If you add locking, tell gdb (rdar://7516456).
**********************************************************************/
Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}

Continue to click the getIsa() method in the objc-object.h file

inline Class
objc_object::getIsa() 
{
    if (fastpath(!isTaggedPointer())) return ISA();

    extern objc_class OBJC_CLASS_$___NSUnrecognizedTaggedPointer;
    uintptr_t slot, ptr = (uintptr_t)this;
    Class cls;

    slot = (ptr >> _OBJC_TAG_SLOT_SHIFT) & _OBJC_TAG_SLOT_MASK;
    cls = objc_tag_classes[slot];
    if (slowpath(cls == (Class)&OBJC_CLASS_$___NSUnrecognizedTaggedPointer)) {
        slot = (ptr >> _OBJC_TAG_EXT_SLOT_SHIFT) & _OBJC_TAG_EXT_SLOT_MASK;
        cls = objc_tag_ext_classes[slot];
    }
    return cls;
}

which can be simplified to

inline Class
objc_object::getIsa() 
{
    if (!isTaggedPointer()) return ISA();
    return cls;
}

 Go ahead and click ISA()方法,in the objc-object.h file

In  Apple's official open source objc4-787.1 version

#if SUPPORT_NONPOINTER_ISA

inline Class 
objc_object::ISA() 
{
    ASSERT(!isTaggedPointer()); 
#if SUPPORT_INDEXED_ISA
    if (isa.nonpointer) {
        uintptr_t slot = isa.indexcls;
        return classForIndex((unsigned)slot);
    }
    return (Class)isa.bits;
#else
    return (Class)(isa.bits & ISA_MASK);
#endif
}

In  Apple's official open source objc4-818.2 version

//objc-object.h文件
inline Class
objc_object::ISA(bool authenticated)
{
    ASSERT(!isTaggedPointer());
    return isa.getDecodedClass(authenticated);
}


//objc-object.h文件
inline Class
isa_t::getDecodedClass(bool authenticated) {
#if SUPPORT_INDEXED_ISA
    if (nonpointer) {
        return classForIndex(indexcls);
    }
    return (Class)cls;
#else
    return getClass(authenticated);
#endif
}


//objc-object.h文件
inline Class
isa_t::getClass(MAYBE_UNUSED_AUTHENTICATED_PARAM bool authenticated) {
#if SUPPORT_INDEXED_ISA
    return cls;
#else

    uintptr_t clsbits = bits;

#   if __has_feature(ptrauth_calls)
#       if ISA_SIGNING_AUTH_MODE == ISA_SIGNING_AUTH
    // Most callers aren't security critical, so skip the
    // authentication unless they ask for it. Message sending and
    // cache filling are protected by the auth code in msgSend.
    if (authenticated) {
        // Mask off all bits besides the class pointer and signature.
        clsbits &= ISA_MASK;
        if (clsbits == 0)
            return Nil;
        clsbits = (uintptr_t)ptrauth_auth_data((void *)clsbits, ISA_SIGNING_KEY, ptrauth_blend_discriminator(this, ISA_SIGNING_DISCRIMINATOR));
    } else {
        // If not authenticating, strip using the precomputed class mask.
        clsbits &= objc_debug_isa_class_mask;
    }
#       else
    // If not authenticating, strip using the precomputed class mask.
    clsbits &= objc_debug_isa_class_mask;
#       endif

#   else
    clsbits &= ISA_MASK;
#   endif

    return (Class)clsbits;
#endif
}

We can all simplify this to

inline Class 
objc_object::ISA() 
{
    return (Class)(isa.bits & ISA_MASK);
}

Tracing back to the source, we see that the instance method  - (CLass) class finally gets: the structureobjc_objectisa.bits & ISA_MASK

接下来解释一下

1、inline关键字作用
2、在方法objc_object::ISA() 中双冒号::的作用
3、objc_object中的isa
4、isa.bits & ISA_MASK

1、inline关键字

The inline function used to define a class, the main reason for introducing it is to use it to replace the macro definition in the expression form in C.

Modified with inlinekeywords are inline functions, and inline functions are used to replace macro definitions. The reason for superseding the macro definition is:

(1) The reason for using the form of define in C is because C language is a very efficient language. This kind of macro definition is like a function in form and use, but it is implemented by the preprocessor, and there is no A series of operations such as parameter push and code generation are very efficient, which is one of the main reasons why it is used in C.
(2) This kind of macro definition is similar to a function in form, but when using it, it is just a simple replacement in the preprocessor symbol table, so it cannot check the validity of parameters, and it cannot enjoy C++ compilation In addition, its return value cannot be cast into a suitable type that can be converted, so its use has a series of hidden dangers and limitations.
(3) Class and class access control are introduced in C++, so that if an operation or an expression involves a protected member or private member of a class, you cannot use this macro definition to implement it (because you cannot use the this pointer in place).
(4) The purpose of inline is to replace the macro definition in the expression form. It eliminates the shortcomings of the macro definition and at the same time inherits the advantages of the macro definition well.

2、方法objc_object::ISA() 中双冒号::

双冒号::It is used to represent a "domain operator". For example: a class A is declared, and a member function void f() is declared in class A, but the definition of f is not given in the class declaration, then when f is defined outside the class , it must be written as void A::f(), indicating that the f() function is a member function of class A.

3、objc_object中的isa指针

根据objc_objectThe definition of isa, we can know that isa is actually an isa_tobject, continue to see isa_tthe implementation:

union isa_t 
{
//这里省略很多变量
}

isa_tIt is a union, that is to say: objc_object in isafact it is a structure 

4、isa.bits & ISA_MASK

isaIt is a union, and its internal attribute bits

union isa_t 
{
   //省略部分方法和属性...
    uintptr_t bits;
}

Then look at uintptr_tthe implementation:

typedef unsigned long       uintptr_t;

Found that it is a unsigned longtype, ISA_MASKthe definition:

# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
# else
#   error unknown architecture for packed isa
# endif

In fact, ISA_MASKit is a numeric type. That is to say, judging whether two objects are the same classis actually objc_objectobtained by comparing the results of numerical calculations in the comparison.

3. The relationship between the isa pointer and Object (objc instance object), Class (class), Metaclass (metaclass), Rootclass (root class), Rootclass's metaclass (root metaclass)

1. Subclass, parent class instance object test code:

    //继承关系: Man : People : NSObject(Man继承自People继承自NSObject)
    NSObject *object_instance = [[NSObject alloc]init];
    People *people = [[People alloc]init];
    Man *man = [[Man alloc]init];
    
    NSLog(@"object_instance----     %p" , object_instance);
    NSLog(@"people----              %p" , people);
    NSLog(@"man----                 %p" , man);

The first address of the three instance objects, the output result:

Print the memory content of 3 instance objects

According to objc_objectthe struct structure, the corresponding memory content should be Classcontent, which is one isa, but here isais initialized isa(that is, contains the pointed Clsinformation and some other information). We can & 0x0000000ffffffff8ULLtake out the corresponding isapointing Classinformation as follows

 The corresponding $0 $1 $2is the content pointed to by isa, poclick

 Continue to view the memory distribution of class objects, the corresponding commands and results are as follows

we will find

(lldb) x/2gx 0x00007fff800d0660 //NSObject元类地址
0x7fff800d0660: 0x00007fff800d0638 0x0000000000000000 //根元类地址 + 父类地址(=NSObject类对象的地址)
(lldb) x/2gx 0x0000000100728798 //People元类地址
0x100728798: 0x0000000100728770 0x00007fff800d0660 //根元类地址 + 父类地址(=People父类的元类地址)
(lldb) x/2gx 0x0000000100728748 //Man元类地址
0x100728748: 0x0000000100728720 0x0000000100728798 //根元类地址 + 父类地址(=Man父类的元类地址)

All metaclasses isapoint to the class object NSObject 对应的元类that we call the pointer 根元类
of the root metaclass , and the pointers of other child metaclasses point to the metaclass of the corresponding parent class.superclassNSObjectsuperclass

2. Instance object, class object, metaclass object test code:

//自定义
struct objc_classA{
  Class isa;
};

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //isa指针,实例对象,类,元类,根类,根元类
    [self testISAForObjc];
}

//isa指针,实例对象,类,元类,根类,根元类
- (void)testISAForObjc {
    Person *p =  [[Person alloc]init];//Person的实例对象
          
    Class pClass = object_getClass(p);//Person的类对象
      
    struct  objc_classA *pClassA = (__bridge struct objc_classA *)(pClass);//使用结构体转化,拿到isa
      
    Class metaPClass = object_getClass(pClass);//Person的元类对象
    
    NSLog(@"p----           %p" , p);
    NSLog(@"pClass----      %p" , pClass);
    NSLog(@"pClassA----     %p" , pClassA);
    NSLog(@"metaPClass----  %p" , metaPClass);
}

The output is as follows:
 

1. The first address of the isa pointer of the instance object is the first address of the instance object

2. The first address of the isa pointer of the class object is the first address of the class object

3. The first address of the isa pointer of the metaclass object is the first address of the metaclass object (you can see it in the same way)

4. The isa of the instance object points to the class object

5. The isa of the class object points to the metaclass object

From this we can see that the main link channel of instances, classes, and metaclasses is their isapointers

(1) The pointing of the isa pointer (that is, the connection between isa and instances, classes, and metaclasses)

The isa pointer of an objc instance object points to its class object (that is, the class we usually call), the isa pointer of the class object points to its metaclass, the isa pointer of the metaclass points to the root metaclass, and all metaclass isa points to the same A root metaclass whose isa pointer points to the root metaclass itself. The root metaclass super class parent class points to the NSObject class object.

The isa of objc/instance points to the class: when calling the object method, find the class object through the isa of the instance object, and finally find the object method in the class object, and then execute the corresponding object method. The isa of the class points to the meta-class: when calling the
class method, find the metaclass object through the isa of the class object, and finally find the class method in the metaclass object, and then execute the corresponding class method

To sum up the rules of isa pointing and inheritance relationship among instance objects, class objects and metaclass objects are:

1. The isa of the instance object points to the class, and the isa of the class points to the metaclass (metaClass)
2. The superClass of the class points to its parent class. If the class is the root class, the value is nil
3. The isa of the metaclass points to the root metaclass, if If the metaclass is the root metaclass, it points to itself
4. The superClass of the metaclass points to the parent metaclass, and if the root metaclass points to the root class

(2) isa pointer function

1. Call an object method of an object

It will first look for the method in the methodLists of objc_class (class) pointed to by its own isa pointer. If it cannot find it, it will find its parent class through the super_class pointer of objc_class (class), and then find the method from its methodLists. If still If it cannot be found, continue to search in the parent class structure of the next level through super_class until the root class

2. Call a class method

It will first find the metaclass (metaclass) through its own isa pointer, and find the class method from its methodLists. If it cannot find it, it will find the metaclass (metaclass) structure of the parent class through the super_class pointer of the metaclass (metaclass) body, and then look up the method from methodLists, if still not found, continue to look up the parent class structure through super_class until the root metaclass (metaclass);

Attention to detail:

When running, the compiler will convert the code into objc_msgSend(obj, @selector (makeText)). In the objc_msgSend function, first find the class (class) corresponding to obj (object) through the isa pointer of obj (object). In the class (class), first go to the cache to find the corresponding method (method) through SEL (method number). If it is not found in the cache, then go to the methodLists to find it. If it is not found in the methodists, go to the superClass to find it. If it can If found, the method (method) is added to the cache to facilitate the next search, and the function pointer in the method (method) is used to jump to the corresponding function for execution.

GitHub sample code Demo

Reference article:
runtime (2) of iOS development: Analysis of the Class of NSObject object

OC-lower layer implements isa pointer

The location of isa and what it does

Guess you like

Origin blog.csdn.net/MinggeQingchun/article/details/117886677