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.
二、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_object
isa.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 inline
keywords 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_object
The definition of isa, we can know that isa is actually an isa_t
object, continue to see isa_t
the implementation:
union isa_t
{
//这里省略很多变量
}
isa_t
It is a union, that is to say: objc_object
in isa
fact it is a structure
4、isa.bits & ISA_MASK
isa
It is a union, and its internal attribute bits
union isa_t
{
//省略部分方法和属性...
uintptr_t bits;
}
Then look at uintptr_t
the implementation:
typedef unsigned long uintptr_t;
Found that it is a unsigned long
type, ISA_MASK
the 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_MASK
it is a numeric type. That is to say, judging whether two objects are the same class
is actually objc_object
obtained 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_object
the struct structure, the corresponding memory content should be Class
content, which is one isa
, but here isa
is initialized isa
(that is, contains the pointed Cls
information and some other information). We can & 0x0000000ffffffff8ULL
take out the corresponding isa
pointing Class
information as follows
The corresponding $0 $1 $2
is the content pointed to by isa, po
click
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 isa
point 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.superclass
NSObject
superclass
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 isa
pointers
(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