Click here for more information
1. An overview of the
Speaking of ISA, I think to some of the friends who have explored the bottom, should not be unfamiliar, and to some of the friends who have not explored, there are some clouds in the fog, what is this thing, what to use, at ordinary times in the development is also useless ah?
It’s true that isa is not directly used in development, but it does work on the ground all the time, so if it works, what does it look like and how does it work? In this article, we find out.
2. The isa structure
Let’s first look at the structure of ISA, 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
Isa isa union of uintptr_t which is defined as follows:
typedef unsigned long uintptr_t;
Copy the code
Uintptr_t is actually a long and it takes up 8 segments.
There’s a struct, and in that struct there’s a macro that defines ISA_BITFIELD, so let’s look at that:
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t deallocating : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 19
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
# 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)
# else
# error unknown architecture for packed isa
# endif
Copy the code
From the above macro definition, it can be seen that the content of the definition is slightly different for different platforms. I debug with the simulator, so let’s take a look at the macro definition under X86_64, replace the macro definition in the ISA structure, and simplify as follows:
union isa_t {
isa_t() { }
isa_t(long value) : bits(value) { }
Class cls;
long bits;
#if defined(ISA_BITFIELD)
struct {
long nonpointer : 1;
long has_assoc : 1;
long has_cxx_dtor : 1;
long shiftcls : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/
long magic : 6;
long weakly_referenced : 1;
long deallocating : 1;
long has_sidetable_rc : 1;
long extra_rc : 8
};
#endif
};
Copy the code
There is also a CLS member variable whose type is Class. What is this? Look at this:
typedef struct objc_class *Class;
Copy the code
So CLS is a pointer.
We look at the overall structure of the isa, it is not hard to see, this is an article before we mentioned a consortium domain structure, all the members of the joint in the body of the variable Shared memory space, and its space depends on the size of the footprint the largest member variables, seen from the structure, the CLS is a pointer, 8 bytes, bits are long, 8 bytes, So the whole ISA is 8 bytes, 64 bits.
The combined bitfield is used to save memory space. Let’s take a look at what data is stored in the 64 bits.
3. The principle of the isa
You’ve seen the structure of ISA, but what does it really do? Let’s first look at the underlying structure of the object’s ancestor, NSObject:
@interface NSObject <NSObject> { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wobjc-interface-ivars" Class isa OBJC_ISA_AVAILABILITY; #pragma clang diagnostic pop }Copy the code
From this code, we know that any object that inherits from NSObject, by default, will have a member variable isa, and that isa will always be first in the member list, even with memory optimization.
We can also see this when we look at the underlying code:
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
/// Represents an instance of a class.
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
}
Copy the code
Tracing the underlying code, we know that objc_object has ISA variables, and that objc_class inherits from Objc_Object, so our class also has ISA variables. Future articles will be updated to explore the class.
Now that we know where ISA is, let’s look at what ISA does. Isa is of type Class, so isa refers to a Class, so what is that Class?
In the previous article OC Object low-level Exploration, it is mentioned that after the space of the object is opened up, the ISA of the object needs to be initialized, as follows:
inline void objc_object::initInstanceIsa(Class cls, bool hasCxxDtor) { assert(! cls->instancesRequireRawIsa()); assert(hasCxxDtor == cls->hasCxxDtor()); initIsa(cls, true, hasCxxDtor); } inline void objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) { 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; newisa.has_cxx_dtor = hasCxxDtor; newisa.indexcls = (uintptr_t)cls->classArrayIndex(); #else newisa.bits = ISA_MAGIC_VALUE; newisa.has_cxx_dtor = hasCxxDtor; newisa.shiftcls = (uintptr_t)cls >> 3; #endif isa = newisa; }}Copy the code
In the second method above, nonpointer is true when called, so it goes to the else branch. SUPPORT_INDEXED_ISA is not supported, so the code ends up going to the #else branch. There is an important assignment here:
newisa.shiftcls = (uintptr_t)cls >> 3;
Copy the code
Why is this important? Because shiftCLs records information about a class whose value is shifted 3 bits to the right by CLS. When ISA is initialized, the BITS in ISA contain information about the class.
What about getting information about classes in object ISA? First look at the following runtime API:
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
Copy the code
So this method calls obj->getIsa()
inline Class
objc_object::getIsa()
{
if (!isTaggedPointer()) return ISA();
uintptr_t ptr = (uintptr_t)this;
if (isExtTaggedPointer()) {
uintptr_t slot =
(ptr >> _OBJC_TAG_EXT_SLOT_SHIFT) & _OBJC_TAG_EXT_SLOT_MASK;
return objc_tag_ext_classes[slot];
} else {
uintptr_t slot =
(ptr >> _OBJC_TAG_SLOT_SHIFT) & _OBJC_TAG_SLOT_MASK;
return objc_tag_classes[slot];
}
}
Copy the code
The first judgment in this method is **! IsTaggedPointer ()**, because the current object is not a taggedPointer, the ISA() method is executed.
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 }Copy the code
This method obviously goes to the #else branch, and we get: return (Class)(isa.bits & ISA_MASK); And ISA_MASK is defined as follows:
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# else
# error unknown architecture for packed isa
# endif
Copy the code
Finally, we can understand that: the isa of the object is associated with the information of the class, and the class is obtained by bitwise and operation of isa bits and ISA_MASK.
Here’s a simple test:
int main(int argc, const char * argv[]) {
@autoreleasepool {
GYMPerson *person = [GYMPerson alloc];
object_getClass(person);
}
return 0;
}
Copy the code
We’re object_getClass (person); Select * from ‘LLDB’ where ‘LLDB’ = ‘LLDB’
As you can see from the above, the class of the isa associated object for the object. So the question is what is the ISA associated with the class? Let’s just briefly say that everything is an object, so a class is also an object at the bottom, a class object. Why is that? Look at the underlying code:
struct objc_class : objc_object { // Class ISA; Class superclass; cache_t cache; // formerly cache pointer and vtable class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags class_rw_t *data() { return bits.data(); }}Copy the code
This is just a copy of the code, the underlying class inherits from objc_object, so how many class objects are created in memory? Who created it? A class object is created by the system at compile time and only once in memory, and isa for a class object is the metaclass associated with it.
-(void)classNumberTest {
Class class1 = [GYMPerson class];
Class class2 = [GYMPerson alloc].class;
Class class3 = [GYMPerson alloc].class;
Class class4 = object_getClass([GYMPerson alloc]);
NSLog(@"\nclass1 address = %p\nclass2 address = %p\nclass3 address = %p\nclass4 address = %p\n", class1,class2,class3,class4);
}
Copy the code
The output is:
class1 address = 0x10234c7d8
class2 address = 0x10234c7d8
class3 address = 0x10234c7d8
class4 address = 0x10234c7d8
Copy the code
This confirms that there is only one copy of the class in memory. At this point, we can briefly summarize isa pointing: Isa for objects points to classes, and ISA for classes points to metaclasses.
4. Isa walk
The isa of a class refers to a metaclass. A metaclass is also a class. Where does isa of a metaclass refer to? We will not continue the debugging analysis here, take a look at the official apple graph, as follows:
This diagram contains two directions, one is superclass and one is ISA. Let’s first look at the ISA situation: the OBJECT’s ISA points to the class, the class’s ISA points to the metaclass, the metaclass’s ISA points to the root metaclass, and the root metaclass’s ISA points to the root metaclass (itself). And then there’s the superclass case: you know what the inheritance is, but there are two things that I want to emphasize here, one is that NSObject has a parent class of nil, but the root metaclass has a parent class of NSObject. Apple’s chart is pretty straightforward, so I won’t go over it here.
Ask like iOS small partner attention! Like words to a like it! Thank you very much! Thank you very much! Thank you very much!
— — — — — — — —
Click on the:For more information
Included: Links to original articles