preface
If you want to be an iOS developer, you have to read source code. The following is a small series that the author combed in OC source code exploration — Class and Object, welcome you to read and correct, but also hope to help you.
- Object creation for OC source analysis
- OC source code analysis of ISA
- OC source code analysis and such structural interpretation
- Caching principle of OC source analysis method
- OC source code analysis method search principle
- Analysis and forwarding principle of OC source code analysis method
1. isa
introduce
1.1 isa
What is the
In the OC source code analysis object creation article, we know that alloc will call calloc to allocate memory, followed by initInstanceIsa(CLS, hasCxxDtor), as the name means initialization object ISA, its key code is as follows
inline void
objc_object::initInstanceIsa(Class cls, boolhasCxxDtor) { assert(! cls->instancesRequireRawIsa()); assert(hasCxxDtor == cls->hasCxxDtor());// Pay attention to true here
initIsa(cls, true, hasCxxDtor);
}
inline void
objc_object::initIsa(Class cls, bool nonpointer, boolhasCxxDtor) { assert(! isTaggedPointer());if(! nonpointer) { isa.cls = cls; }else{ assert(! DisableNonpointerIsa); assert(! cls->instancesRequireRawIsa());isa_t newisa(0);
#if SUPPORT_INDEXED_ISA
assert(cls->classArrayIndex() > 0);
newisa.bits = ISA_INDEX_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
newisa.bits = ISA_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.shiftcls = (uintptr_t)cls >> 3;
#endif
// This write must be performed in a single store in some cases
// (for example when realizing a class because other threads
// may simultaneously try to use the class).
// fixme use atomics here to guarantee single-store and to
// guarantee memory order w.r.t. the class index table
/ /... but not too atomic because we don't want to hurt instantiationisa = newisa; }}Copy the code
- about
Tagged Pointer
It is said that Apple introduced Tagged Pointer to save memory and improve execution efficiency. For 64-bit applications, the introduction of Tagged Pointer reduces memory usage by half, increases access speed by 3 times, and increases creation and destruction speed by 100 times.
Tagged Pointer was first used on the iPhone 5S and is now used almost everywhere. Tagged Pointer Tagged Pointer Tagged Pointer Tagged Pointer Tagged Pointer
isa_t
type
Click isa in objc_object::initIsa() to see that ISA is isa_T
struct objc_object {
private:
isa_tisa; .// Some public and private methods
};
Copy the code
And isa_T is actually a union.
So let’s just generalize the distinction between structs and unions
- Both can contain multiple different types of data, such as
int
,double
,Class
And so on.- in
struct
Each member has its own memory spacestruct
The total memory length of a variable is greater than or equal to the sum of the memory lengths of each member. And in theunion
In, each member shares a memory space, oneunion
The total memory length of a variable is equal to the memory length of the member with the longest memory.- right
struct
The values of the members in the. rightunion
Only one member can be assigned at a time, and the values of other members will not exist.
Isa_t contains two member variables CLS and BITS, which are structured as follows
union isa_t {
isa_t() {}isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
#if defined(ISA_BITFIELD)
struct {
ISA_BITFIELD; // defined in isa.h
};
#endif
};
Copy the code
1.2 isa
thebits
Member variables
- A domain
Let me introduce you to the concept of bitfields
A bit field is a data structure that stores data in compact bits and allows programmers to manipulate the bits of the structure.
- Advantages:
- Save storage space;
- Easy access to parts of an integer value can simplify the program’s source code.
- Disadvantages:
- The way its memory allocation and alignment are implemented depends on the specific machine and system, and may have different results on different platforms, resulting in bitfields being inherently non-portable.
The bits member variable of ISA is of type uintptr_t, which is essentially an unsigned long
typedef unsigned long uintptr_t;
Copy the code
In 64-bit CPU architecture, bits are 64 bits, or 8 bytes, and their individual bits are stored in a bit field, ISA_BITFIELD.
ISA_BITFIELD
Next, take a look at the source code of ISA_BITFIELD (since THE author is using macOS project to study OC underlying source code, so here x86_64 architecture as an example)
# 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)
Copy the code
To be clear, the ISA pointer is also 8 bytes long on a 64-bit CPU architecture, which can store a lot of content. Apple uses only part of the bits (44 under X86_64, 33 under ARM64) to store the class address for optimal performance, with the rest of the bits used to store some other information.
Specific analysis of the meaning of each member of the ISA_BITFIELD bit field:
nonpointer
: indicates whether it is correctIsa pointer
Enable pointer optimization.- 0: not optimized, pure
Isa pointer
When the accessisa
Pointer directly returns its member variablecls
- 1: optimization, i.e
Isa pointer
The content not only contains the class address, but also contains some information about the class, reference count of the object, and so on.
- 0: not optimized, pure
has_assoc
: Indicates whether there is an associated object.has_cxx_dtor
: whether the object has a destructor for C++ or Objc.- If there is a destructor, you need to do some logical processing of the destructor.
- If not, the object can be released more quickly.
shiftcls
: Storage class address. Turn on pointer optimization in case ofx86_64
Architecture has44Used to store class addresses,arm64
In the architecture33 。magic
: used by the debugger to determine whether the current object is a real object or an uninitialized space.weakly_referenced
: Indicates whether an object is or has been pointed to oneARC
Objects without weak references are freed faster.deallocating
: Indicates whether the object is freeing memory.has_sidetable_rc
: Whether the reference count value of the object is carried.extra_rc
: represents the referential count value of the object.extra_rc
Just store the extra reference count, the actual reference count formula:Actual reference count = EXTRA_RC + 1
. This takes up 8 bits, so the maximum reference count you can theoretically store is:2^8-1 + 1 = 256
(arm64
CPU architectureextra_rc
With 19 bits, the maximum reference count that can be stored is2^19 - 1 + 1 = 524288
).- with
has_sidetable_rc
When the maximum reference count of an object exceeds the threshold,has_sidetable_rc
Is 1, or 0 otherwise
- with
1.3 isa
thecls
Member variables
After analyzing isa’s bitfields, only CLS is left, which is of Class type and also source code
typedef struct objc_class *Class;
// let's take a look at the type of id. Obviously id isa pointer variable, and its value isa single isa variable
typedef struct objc_object *id;
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache;
class_data_bits_t bits;
class_rw_t *data() {
returnbits.data(); }...// Some methods
};
struct objc_object {
private:
isa_tisa; .// Some public and private methods
};
Copy the code
Class isa pointer to the objc_class structure, which in turn inherits from objc_object. Therefore, the value of the Class pointer has an isa member variable (isa_t). This ISA member variable is 8 bytes on a 64-bit CPU architecture and ranks in the first 8 bytes of the objC_class structure.
1.4 isa
The role of
As can be seen from the objC_Object structure, when the system allocates memory for an object and initializes the instance variables, the first object in the structure of the instance variables of these objects is ISA.
At the same time, we know that Shiftcls stores class addresses through the bitfield specification for ISA. In the X86_64 architecture, Shiftcls occupies bit 44, or bit 3, 46. Fill all the [3, 46] bits with 1, [0, 2] bits and [47, 63] bits with 0 to get 0x7FFFFFFFFFF8, which is the value of ISA_MASK. Therefore, ISA & ISA_MASK will get the class address stored by Shiftcls. That’s what masks are for.
As shown in the figure below
Here is an example of what ISA can do
In this case, run the LLDB command to debug
Description:
0x001d800100001129
isObject p
theisa
Value, throughisa & ISA_MASK
computed0x0000000100001128
isPerson
The address of the class- Proof [1] : Pass
p/x Person.class
Print directlyPerson
Class address, of course0x0000000100001128
, so [1] prove true!
Conclusion:isa
Associating objects with classes acts as a bridge between them.
Note: The analysis of Shiftcls is based on nonpointer being 1. If nonpointer is 0, the entire ISA stores CLS.
Consider: if you don’t use ISA_MASK, how do you justify isa for this purpose? Answers will be added at the end of this article.
1.5 isa
Initialization supplement of
A final note on isa initialization. Remember the entry to initialize ISA? Is initIsa (CLS, true, hasCxxDtor); The value of nonpointer is true and SUPPORT_INDEXED_ISA is defined
#if__ARM_ARCH_7K__ >= 2 || (__arm64__ && ! __LP64__)
# define SUPPORT_INDEXED_ISA 1
#else
# define SUPPORT_INDEXED_ISA 0
#endif
Copy the code
Under x86_64, SUPPORT_INDEXED_ISA is 0, so the initialization of ISA will eventually come
isa_t newisa(0);
// Assign the value to bits using ISA_MAGIC_VALUE(0x001d800000000001ULL)
// Nonpointer is 1, magic is 1d, and other variables are zero
newisa.bits = ISA_MAGIC_VALUE;
// hasCxxDtor is fetched from class ISA
newisa.has_cxx_dtor = hasCxxDtor;
// shiftcls 3 bits to the right and assign to shiftcls
newisa.shiftcls = (uintptr_t)cls >> 3;
isa = newisa;
Copy the code
I won’t go into hasCxxDtor here because of space.
2 isa
Pointing to the figure
Through the above source code analysis, we realize that the isa pointer to an object points to the class to which the object belongs. The class itself has an ISA pointer. What does it point to?
This is where the concept of meta Class comes in. Let’s take a look at metaclasses first
In OC, methods of objects are not stored in the structure of objects (if each object holds methods that it can execute, this can have a significant impact on memory usage).
When an instance method of an object is called, it looks up the corresponding class through its own ISA and then looks up the implementation of the corresponding method in the class_datA_bits_t structure of the owning class. Also, each objC_class has a superclass pointer to its superclass to look up inherited methods.
What happens when a class method is called? The solution to this OC is to introduce metaclasses to ensure that class methods can be found through the same mechanism. That is, the ISA of a class points to a metaclass.
Apple officially has an ISA orientation chart, i.e
So let’s verify that.
2.1 Preparations
Create the Teacher class, where the Person class inherits from NSObject and the Teacher class inherits from the Person class.
Teacher equals Subclass, Person equals Superclass, and NSObject equals Root class.
2.2 Verification Process
- To obtain
teacher
Object class (the result isTeacher
Class at the address0x0000000100001230
)
(lldb) x/4gx teacher
0x100f59400: 0x001d800100001231 0x0000000000000000
0x100f59410: 0x636f72504b575b2d 0x70756f7247737365
(lldb) p/x 0x001d800100001231 & 0x00007ffffffffff8 // isa & ISA_MASK
(long) $3 = 0x0000000100001230 // Class address of teacher object
(lldb) po $3
Teacher // Class object teacher
Copy the code
- To obtain
Teacher
Class metaclass (the result isThe Teacher metaclass
, the address is0x0000000100001208
)
(lldb) x/4gx Teacher.class
0x100001230: 0x001d800100001209 0x00000001000011e0
0x100001240: 0x0000000100f61150 0x0000000100000003
(lldb) p/x 0x001d800100001209 & 0x00007ffffffffff8 // isa & ISA_MASK
(long) $5 = 0x0000000100001208 // Teacher class metaclass address
(lldb) po $5
Teacher // Teacher class metaclass
Copy the code
The Teacher class and the Teacher metaclass address are different
- To obtain
person
Object class (the result isPerson
Class at the address0x00000001000011e0
), and the metaclass of the class (the result isThe Person metaclass
, the address is0x00000001000011b8
)
(lldb) x/4gx person
0x100f60a30: 0x001d8001000011e1 0x0000000000000000
0x100f60a40: 0x0000000000000002 0x00007fff9b855588
(lldb) p/x 0x001d8001000011e1 & 0x00007ffffffffff8 // isa & ISA_MASK
(long) $8 = 0x00000001000011e0 The class address of the person object
(lldb) po $8
Person // The class of the object Person
(lldb) x/4gx Person.class
0x1000011e0: 0x001d8001000011b9 0x0000000100b38140
0x1000011f0: 0x0000000100f61030 0x0000000100000003
(lldb) p/x 0x001d8001000011b9 & 0x00007ffffffffff8 // isa & ISA_MASK
(long) $10 = 0x00000001000011b8 // Metaclass address of the Person class
(lldb) po $10
Person // Metaclass of the Person class
Copy the code
- To obtain
object
Object class (the result isNSObject
Class at the address0x0000000100b38140
), and the metaclass of the class (the result isNSObject metaclass
, the address is0x0000000100b380f0
)
(lldb) x/4gx object
0x100f5cc50: 0x001d800100b38141 0x0000000000000000
0x100f5cc60: 0x70736e494b575b2d 0x574b57726f746365
(lldb) p/x 0x001d800100b38141 & 0x00007ffffffffff8 // isa & ISA_MASK
(long) $12 = 0x0000000100b38140 // The class address of object
(lldb) po $12
NSObject // The class of object
(lldb) x/4gx NSObject.class
0x100b38140: 0x001d800100b380f1 0x0000000000000000
0x100b38150: 0x0000000101913060 0x0000000200000003
(lldb) p/x 0x001d800100b380f1 & 0x00007ffffffffff8 // isa & ISA_MASK
(long) $14 = 0x0000000100b380f0 // The metaclass address of NSObject
(lldb) po $14
NSObject // the NSObject metaclass
Copy the code
- To obtain
The Teacher metaclass
The metaclass,The Person metaclass
The metaclass, as well asNSObject metaclass
The metaclass
(lldb) x/4gx 0x0000000100001208 / / the Teacher metaclass
0x100001208: 0x001d800100b380f1 0x00000001000011b8
0x100001218: 0x000000010186f950 0x0000000400000007
(lldb) p/x 0x001d800100b380f1 & 0x00007ffffffffff8 // isa & ISA_MASK
(long) $16 = 0x0000000100b380f0 / / NSObject metaclass
(lldb) po $16
NSObject / / NSObject metaclass
(lldb) x/4gx 0x00000001000011b8 / / Person metaclass
0x1000011b8: 0x001d800100b380f1 0x0000000100b380f0
0x1000011c8: 0x0000000101905a50 0x0000000300000007
(lldb) p/x 0x001d800100b380f1 & 0x00007ffffffffff8 // isa & ISA_MASK
(long) $17 = 0x0000000100b380f0 / / NSObject metaclass
(lldb) po $17
NSObject / / NSObject metaclass
(lldb) x/4gx 0x0000000100b380f0 / / NSObject metaclass
0x100b380f0: 0x001d800100b380f1 0x0000000100b38140
0x100b38100: 0x0000000101903820 0x0000000500000007
(lldb) p/x 0x001d800100b380f1 & 0x00007ffffffffff8 // isa & ISA_MASK
(long) $18 = 0x0000000100b380f0 / / NSObject metaclass
(lldb) po $18
NSObject / / NSObject metaclass
Copy the code
2.3 isa
Point to the conclusion
Based on the verification process of [2.2], it can be concluded that:
- The object’s
Isa pointer
Refers to the class to which the object belongs (e.gperson
The object’sisa
Point to thePerson
Class) - Of the class
Isa pointer
A metaclass pointing to a class (e.gPerson
Of the classisa
Point to theThe Person metaclass
) - metaclass
Isa pointer
Points to the root metaclass (e.gThe Person metaclass
theisa
Point to theNSObject metaclass
)NSObject class
Class is the yuanA metaclass
NSObject metaclass
theIsa pointer
Pointing to itself (it’s a circle)
Consider: If the Person class inherits from NSProxy, what does the isa reference look like? Those who are interested can try it.
2.4 Proof of inheritance relationship
Class inheritance relationship proof process :(use -> to indicate inheritance from)
(LLDB) p class_getSuperclass(teacher.class) (class) $19 = Person // Teacher -> Person (LLDB) p Class_getSuperclass (person.class) (class) $20 = NSObject // Person class -> NSObject (LLDB) p Class_getSuperclass (NSObject. Class) (class) $21 = nil // NSObject class -> nilCopy the code
Proof of inheritance relation of metaclass :(with -> to indicate inheritance from)
// 0x0000000100001208 is Teacher metaclass (LLDB) p/x class_getSuperclass((Class)0x0000000100001208) (Class) $17 = 0x00000001000011B8 // Person metaclass (LLDB) Po $17 Person // Teacher metaclass -> Person metaclass // 0x00000001000011B8 is Person metaclass (LLDB) P /x Class_getSuperclass ((Class) 0x00000001000011B8) (Class) $22 = 0x0000000100B380F0 // NSObject meta-class (root meta-class) (LLDB) Po $22 NSObject Person metaclass -> root metaclass // 0x0000000100B380F0 is the root metaclass (LLDB) p/x class_getSuperclass((Class) 0x0000000100b380F0) (Class) $23 = 0x0000000100B38140 NSObject Class (root Class) (LLDB) Po $23 NSObject root metaclass -> root ClassCopy the code
The root metaclass inherits from the root class (NSObject metaclass -> NSObject class) and the root class inherits from nil (NSObject class -> nil)
2.5 isa
Point to the doodle version of the diagram
Apply the above example to the ISA pointing diagram to get the following image
3. Summary
isa
isisa_t
Structure, adoptedUnion + bit-fieldDisplay different content in different bits to save storage space and optimize memory.isa
Contains thecls
andbits
Two member variables, the two member variables inA 64 - bit
CPU architecture is always 8 bytes long, soisa
inA 64 - bit
The length is also 8 bytes in CPU architecture.isa
The bitfield stores some object and class information, and associates the object and class, plays the role of intermediate bridge.isa
Conclusions related to the pointing graph:- The object’s
Isa pointer
Refers to the class to which the object belongs (e.gperson
The object’sisa
Point to thePerson
Class) - Of the class
Isa pointer
A metaclass pointing to a class (e.gPerson
Of the classisa
Point to theThe Person metaclass
) - metaclass
Isa pointer
Points to the root metaclass (e.gThe Person metaclass
theisa
Point to theNSObject metaclass
)A metaclass
theIsa pointer
Pointing to itself (it’s a circle)
- Inheritance relationships of metaclasses are passed upward (e.g
The Teacher metaclass
Inherited fromThe Person metaclass
)A metaclass
Inherited fromThe root class
The root class
Inherited fromnil
- The object’s
Added 4.
4.1 isa
Proof of the effect of 2
Q: If you don’t use ISA_MASK, how do you prove that ISA relates the role of objects and classes?
A: Shiftcls is 44 bits long in x86_64 and stored in [3, 46] bits of ISA. Therefore, by clearing [0, 2] bits and [47, 63] bits of ISA, shiftcls value can also be obtained to determine the class.
As shown in the figure, an operation on ISA successfully yields the same address as the Person class.
4.2 NSProxy
theisa
Point to the
Q: If the Person class inherits from NSProxy, what does the isa reference look like?
A: Like NSObject, both are root classes.
The resources
Learning about ISA from NSObject Initialization (by DraP)
PS
- The source code project has been placed
github
The stamp, pleaseObjc4-756.2 – the source code - You can also download apple’s official ObjC4 source code to study.
- Reprint please indicate the source! Thank you very much!