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.
二、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_object
isa.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 inline
Schlü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_object
Die Definition von isa, wir können wissen, dass isa eigentlich ein isa_t
Objekt ist, sehen Sie weiterhin isa_t
die Implementierung:
union isa_t
{
//这里省略很多变量
}
isa_t
Es ist eine Vereinigung, das heißt: es ist objc_object
tatsächlich isa
eine Struktur
4、isa.bits & ISA_MASK
isa
Es ist eine Union und ihre internen Attributbits
union isa_t
{
//省略部分方法和属性...
uintptr_t bits;
}
Dann schauen Sie sich uintptr_t
die Implementierung an:
typedef unsigned long uintptr_t;
Gefunden, dass es sich um einen unsigned long
Typ handelt, ISA_MASK
die 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_MASK
ist es ein numerischer Typ. Das heißt, die Beurteilung, ob zwei Objekte gleich sind, class
wird tatsächlich objc_object
erhalten, 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_object
der Struct-Struktur sollte der entsprechende Speicherinhalt Class
content sein, der eins ist isa
, aber hier isa
initialisiert wird isa
(d. h. die angezeigten Cls
Informationen und einige andere Informationen enthält). Wir können die entsprechenden Hinweisinformationen wie folgt & 0x0000000ffffffff8ULL
entnehmenisa
Class
Das entsprechende $0 $1 $2
ist der Inhalt, auf den isa, po
click 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 isa
zeigen 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.根元类
superclass
NSObject
superclass
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 isa
Zeiger 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