iOS-isa-Zeiger; objc-Instanzobjekt, Klasse, Metaklasse, Root-Klasse, Root-Metaklasse

1. Objekt (objc-Instanzobjekt), Klasse (Klasse), Metaklasse (Metaklasse), Wurzelklasse (Root-Klasse), Metaklasse der Rootclass (Root-Metaklasse)

Um den isa-Zeiger in iOS zu verstehen, können wir auf einige Datenstrukturen von Klassen in Objective-C nicht verzichten; in der Typstruktur von Objective-C sind Object (Instanz), Class (Klasse), Metaclass (Metaklasse), Rootclass (Stammklasse ), die Metaklasse der Rootclass (Root-Metaklasse), und dies sind Objekte.

Werfen Sie einen Blick auf das Objective-C-Objektmodelldiagramm, um Folgendes zu verstehen:

Objective-C---->C/C++----->Assemblersprache---->Maschinensprache; wir wissen, dass Objective-C eine objektorientierte Sprache ist und alle ihre Objekte durch ihre entsprechenden Klassen instanziiert werden Tatsächlich ist die Klasse selbst auch ein Objekt;

In Objective-C sind fast alle von uns verwendeten Klassen Unterklassen der NSObject-Klasse 

Wir öffnen die Datei NSObject.h in der Header-Datei #import <objc/runtime.h> in Xcode oder dem Open-Source-Code von Apple

(Apples offizieller Open-Source -objc4-Quellcode beim Online-Browsing ; objc4-Quellcode-Download , die in diesem Artikel verwendete objc4-818.2-Version)

Das Format der NSObject-Klassendefinition sieht wie folgt aus (wobei die Methodendeklaration ignoriert wird):

@interface NSObject <NSObject> {
    Class isa;
}

Öffnen Sie die Datei objc.h und Sie werden feststellen, dass die Klasse (CLass) und das Instanzobjekt (objc oder Instanz) wie folgt definiert sind:

/// 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实例对象

Klasse ist ein Zeiger auf die Struktur objc_class (Klasse), und id ist ein Zeiger auf die Struktur objc_object (Instanzobjekt) .

Die Klassenstruktur, auf die der isa-Zeiger in objc_object (Instanzobjekt) zeigt, heißt objc_class (die Klasse des Objekts), die gewöhnliche Member-Variablen und Objektmethoden (Methoden beginnend mit „-“) speichert .

Die Klassenstruktur, auf die der isa-Zeiger in objc_class (class) zeigt, wird metaclass (Metaklasse der Klasse) genannt, die Mitgliedsvariablen statischen Typs und Methoden statischen Typs (Methoden beginnend mit „+“) speichert .

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

Die Struktur objc_object (Instanzobjekt) hat nur ein Mitgliedsattribut von isa, das auf objc_class (die Klasse des Objekts) zeigt.

in der Datei runtime.h

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 (Klasse) hat viel mehr Mitglieder als die Struktur objc_object (Instanzobjekt) , und die Funktion jedes Mitglieds wird in der Anmerkung vorgestellt.

Daher finden wir objc-Instanzobjekte, Klassenobjekte, Metaklassen, Root-Klassen, Root-Metaklassen

1. Instanzobjekt (Objekt): Ein Objekt oder Instanzobjekt, das wir erstellt haben, ist eigentlich eine struct objc_object-Struktur, die nur eine Mitgliedsvariable hat, die eine Variable isa vom Typ Klasse ist, und auch ein Strukturzeiger ist Zeiger zeigt auf sein Klassenobjekt (also die Klasse, die wir normalerweise nennen)

Die Layoutstruktur von objc-Objekten im Speicher ist wie folgt:

Strukturdiagramm eines Objective-C-Objekts
ISA-Zeiger
Instanzvariablen der Wurzelklasse
Instanzvariablen der vorletzten Elternklasse
...
Instanzvariablen der übergeordneten Klasse
Klasseninstanzvariablen

Klasse (CLass): speichert zugehörige Daten von Objektinstanzen, wie z. B.: Instanzmethodenliste, Mitgliedsvariablenliste, Attributliste.

Metaklasse (Metaclass): Speichern Sie klassenbezogene Daten, wie z. B.: Klassenmethodenliste, Klasseninformationen usw.

Der Ort von isa und seine Funktion_das erste Bild

二、isa指针

Schauen wir uns noch einmal den Quellcode der Datei NSObject.mm an und sehen uns die Instanzmethode – (CLass)class an

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

Wir erstellen ein Instanzobjekt objc, rufen die Klasse (CLass) auf und rufen tatsächlich die Methode object_getClass auf

Klicken wir auf die Methode object_getClass, um die Quellcodedefinition in der Datei objc-class.mm anzuzeigen

/***********************************************************************
* 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;
}

Klicken Sie weiterhin auf die Methode getIsa() in der Datei objc-object.h

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;
}

was vereinfacht werden kann

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

 Fahren Sie fort und klicken Sie ISA()方法,in die Datei objc-object.h

In  Apples offizieller Open-Source- Version objc4-787.1

#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  Apples offizieller Open-Source- Version objc4-818.2

//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
}

Wir können das alle vereinfachen

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

Wenn wir zurück zur Quelle gehen, sehen wir, dass die Instanzmethode  - Klasse (Klasse) schließlich Folgendes erhält: die Strukturobjc_objectisa.bits & ISA_MASK

接下来解释一下

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

1、inline关键字

Die Inline-Funktion, die zum Definieren einer Klasse verwendet wird. Der Hauptgrund für ihre Einführung besteht darin, sie zum Ersetzen der Makrodefinition in der Ausdrucksform in C zu verwenden.

Geändert mit inlineSchlüsselwörtern sind Inline-Funktionen, und Inline-Funktionen werden verwendet, um Makrodefinitionen zu ersetzen. Der Grund für das Ersetzen der Makrodefinition ist:

(1) Der Grund für die Verwendung der Form von define in C liegt darin, dass die Sprache C eine sehr effiziente Sprache ist. Diese Art der Makrodefinition ist in Form und Verwendung wie eine Funktion, aber sie wird vom Präprozessor implementiert und es gibt kein A Reihe von Operationen wie Parameter-Pushing und Code-Generierung sind sehr effizient, was einer der Hauptgründe ist, warum es in C verwendet wird.
(2) Diese Art von Makrodefinition ähnelt einer Funktion in der Form, aber wenn sie verwendet wird, ist sie nur ein einfacher Ersatz in der Symboltabelle des Präprozessors, sodass sie die Gültigkeit von Parametern nicht überprüfen kann und keine C++-Kompilierung zusätzlich genießen kann , kann sein Rückgabewert nicht gezwungen werden, in einen geeigneten Typ konvertiert zu werden, der konvertiert werden kann, sodass seine Verwendung eine Reihe von versteckten Gefahren und Einschränkungen hat.
(3) Klassen- und Klassenzugriffskontrolle werden in C++ eingeführt, sodass Sie, wenn eine Operation oder ein Ausdruck ein geschütztes Mitglied oder ein privates Mitglied einer Klasse betrifft, diese Art von Makrodefinition nicht verwenden können, um dies zu erreichen (weil es nicht dieser Zeiger sein kann). an Ort und Stelle).
(4) Der Zweck von Inline besteht darin, die Makrodefinition in der Ausdrucksform zu ersetzen.Es beseitigt die Mängel der Makrodefinition und erbt gleichzeitig gut die Vorteile der Makrodefinition.

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

双冒号::Es wird verwendet, um einen "Domänenoperator" darzustellen. Zum Beispiel: Eine Klasse A wird deklariert, und eine Elementfunktion void f() wird in Klasse A deklariert, aber die Definition von f wird nicht in der Klassendeklaration angegeben, wenn dann f außerhalb der Klasse definiert ist, muss sie als void A::f() geschrieben werden, was darauf hinweist, dass die Funktion f() eine Mitgliedsfunktion der Klasse A ist.

3、objc_object中的isa指针

根据objc_objectDie Definition von isa, wir können wissen, dass isa eigentlich ein isa_tObjekt ist, sehen Sie weiterhin isa_tdie Implementierung:

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

isa_tEs ist eine Vereinigung, das heißt: es ist objc_object tatsächlich isaeine Struktur 

4、isa.bits & ISA_MASK

isaEs ist eine Union und ihre internen Attributbits

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

Dann schauen Sie sich uintptr_tdie Implementierung an:

typedef unsigned long       uintptr_t;

Gefunden, dass es sich um einen unsigned longTyp handelt, ISA_MASKdie Definition:

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

Tatsächlich ISA_MASKist es ein numerischer Typ. Das heißt, die Beurteilung, ob zwei Objekte gleich sind, classwird tatsächlich objc_objecterhalten, indem die Ergebnisse numerischer Berechnungen beim Vergleich verglichen werden .

3. Die Beziehung zwischen dem isa-Zeiger und Object (objc-Instanzobjekt), Class (Klasse), Metaclass (Metaklasse), Rootclass (Root-Klasse), Rootclass-Metaklasse (Root-Metaklasse)

1. Unterklasse, Objekttestcode der übergeordneten Klasse:

    //继承关系: 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);

Die erste Adresse der drei Instanzobjekte, das Ausgabeergebnis:

Drucken Sie den Speicherinhalt von 3 Instanzobjekten

Gemäß objc_objectder Struct-Struktur sollte der entsprechende Speicherinhalt Classcontent sein, der eins ist isa, aber hier isainitialisiert wird isa(d. h. die angezeigten ClsInformationen und einige andere Informationen enthält). Wir können die entsprechenden Hinweisinformationen wie folgt & 0x0000000ffffffff8ULLentnehmenisaClass

 Das entsprechende $0 $1 $2ist der Inhalt, auf den isa, poclick zeigt

 Um weiterhin die Speicherverteilung von Klassenobjekten anzuzeigen, lauten die entsprechenden Befehle und Ergebnisse wie folgt

wir werden finden

(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父类的元类地址)

Alle Metaklassen isazeigen auf das Klassenobjekt, das wir den Zeiger der Root-Metaklasse NSObject 对应的元类nennen , und die Zeiger anderer untergeordneter Metaklassen zeigen auf die Metaklasse der entsprechenden Elternklasse.根元类
superclassNSObjectsuperclass

2. Testcode für Instanzobjekt, Klassenobjekt, Metaklassenobjekt:

//自定义
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);
}

Die Ausgabe ist wie folgt:
 

1. Die erste Adresse des isa-Zeigers des Instanzobjekts ist die erste Adresse des Instanzobjekts

2. Die erste Adresse des isa-Zeigers des Klassenobjekts ist die erste Adresse des Klassenobjekts

3. Die erste Adresse des isa-Zeigers des Metaklassen-Objekts ist die erste Adresse des Metaklassen-Objekts (Sie können es genauso sehen)

4. Die isa des Instanzobjekts zeigt auf das Klassenobjekt

5. Die isa des Klassenobjekts zeigt auf das Metaklassenobjekt

Daraus können wir erkennen, dass der Hauptverbindungskanal von Instanzen, Klassen und Metaklassen ihre isaZeiger sind

(1) Das Zeigen des isa-Zeigers (d. h. die Verbindung zwischen isa und Instanzen, Klassen und Metaklassen)

Der isa-Zeiger eines objc-Instanzobjekts zeigt auf sein Klassenobjekt (d. h. die Klasse, die wir normalerweise nennen), der isa-Zeiger des Klassenobjekts zeigt auf seine Metaklasse, der isa-Zeiger der Metaklasse zeigt auf die Root-Metaklasse und so weiter metaclass isa zeigt auf dieselbe A-Root-Metaklasse, deren isa-Zeiger auf die Root-Metaklasse selbst zeigt. Die Elternklasse der Superklasse der Stammmetaklasse zeigt auf das Objekt der Klasse NSObject.

Die isa von objc/instance zeigt auf die Klasse: Wenn Sie die Objektmethode aufrufen, suchen Sie das Klassenobjekt über die isa des Instanzobjekts und finden Sie schließlich die Objektmethode im Klassenobjekt und führen Sie dann die entsprechende Objektmethode aus der Klasse zeigt auf die Metaklasse: Wenn Sie die
Klassenmethode aufrufen, suchen Sie das Metaklassenobjekt über die isa des Klassenobjekts und finden Sie schließlich die Klassenmethode im Metaklassenobjekt und führen Sie dann die entsprechende Klassenmethode aus

Um die Regeln des isa-Zeigens und der Vererbungsbeziehung zwischen Instanzobjekten, Klassenobjekten und Metaklassenobjekten zusammenzufassen:

1. Die isa des Instanzobjekts zeigt auf die Klasse und die isa der Klasse zeigt auf die Metaklasse (metaClass)
2. Die superClass der Klasse zeigt auf ihre Elternklasse Wenn die Klasse die Wurzelklasse ist, ist der Wert nil
3. Die isa der Metaklasse zeigt auf die Root-Metaklasse, wenn die Metaklasse die Root-Metaklasse ist, zeigt sie auf sich selbst.
4. Die SuperClass der Metaklasse zeigt auf die Eltern-Metaklasse, und wenn die Root-Metaklasse auf die Root-Klasse zeigt

(2) ist eine Zeigerfunktion

1. Rufen Sie eine Objektmethode eines Objekts auf

Es wird zuerst nach der Methode in den Methodenlisten von objc_class (Klasse) suchen, auf die durch seinen eigenen isa-Zeiger gezeigt wird. Wenn es sie nicht finden kann, wird es seine Elternklasse durch den super_class-Zeiger von objc_class (Klasse) finden und dann die Methode finden aus seinen methodLists.Wenn immer noch Wenn es nicht gefunden werden kann, fahren Sie mit der Suche in der übergeordneten Klassenstruktur der nächsten Ebene durch super_class bis zur Wurzelklasse fort

2. Rufen Sie eine Klassenmethode auf

Es wird zuerst die Metaklasse (Metaklasse) durch seinen eigenen isa-Zeiger finden und die Klassenmethode aus seinen methodLists finden.Wenn es sie nicht finden kann, wird es die Metaklasse (Metaklasse)-Struktur der Elternklasse durch den super_class-Zeiger der Metaklasse finden (metaclass) body, und suchen Sie dann die Methode in methodLists nach, wenn sie immer noch nicht gefunden wird, fahren Sie fort, die übergeordnete Klassenstruktur über super_class nachzuschlagen, bis die Root-Metaklasse (metaclass);

Aufmerksamkeit fürs Detail:

Beim Ausführen konvertiert der Compiler den Code in objc_msgSend(obj, @selector (makeText)).Suchen Sie in der objc_msgSend-Funktion zuerst die Klasse (class), die obj (object) entspricht, durch den isa-Zeiger von obj (object). Gehen Sie in der Klasse (Klasse) zuerst zum Cache, um die entsprechende Methode (Methode) über SEL (Methodennummer) zu finden. Wenn sie nicht im Cache gefunden wird, gehen Sie zu den Methodenlisten, um sie zu finden. Wenn sie nicht gefunden wird Gehen Sie in den Methodisten zur superClass, um sie zu finden.Wenn sie gefunden wird, wird die Methode (Methode) dem Cache hinzugefügt, um die nächste Suche zu erleichtern, und der Funktionszeiger in der Methode (Methode) wird verwendet, um zu der zu springen entsprechende Funktion zur Ausführung.

GitHub-Beispielcode-Demo

Referenzartikel:
Laufzeit (2) der iOS-Entwicklung: Analyse der Klasse des NSObject-Objekts

Die untere OC-Schicht implementiert einen Zeiger

Der Standort von isa und was es tut

Ich denke du magst

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