Today we are going to analyze the principle of OC objects in the last article. Here you will learn the following contents:
1. The underlying nature of objects
2. Commonwealth bitfield
3, Isa and Class
4, isa Class assignment process (by bit operation to get the Class address) reference article: C bit field
1. The underlying nature of objects
The underlying nature of an object is actually a structure, which we can see with C++ auxiliary code. Remember the clang-rewrite-objc xxx.m directive we used when exploring the underlying principles of blocks? Again, here we’re converting the Person class into C++ to see what’s underneath it.
/******** Person.h ********/
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@end
NS_ASSUME_NONNULL_END
/******** Person.m ********/
#import "Person.h"
@implementation Person
@end
Copy the code
With clang-rewrite-objc person. m we get Person. CPP (note: in the person. CPP file, it’s usually easier to look at Person from the bottom). As we can see in the Person. CPP file, the Person class is a structure at the bottom, so the object created by the Person class is a structure at the bottom.
- Person_IMPL
Person_IMPL
In thestruct NSObject_IMPL NSObject_IVARS;
Corresponding to a structureinheritanceWe also know from the literal meaning that inheritance isMember variable (Ivar), so let’s go in and have a look:You can see it’s aisa
Pointers, which means that, in OC, there’s one for every objectisa
Pointer, because that’s what the system does for us automatically.
1.1 the Class
What’s the Class here? Class is a pointer to a structure:
1.2 id
If you look at the figure above, we also see that
typedef struct objc_object *id;
Copy the code
NSObject at the bottom is objc_Object; When I look at the ID here, IT suddenly makes sense to me why we don’t get an error when we’re writing code to modify objects with ids, such as the id person and so on. Because ID itself is a pointer.
1.3 the set & get
Person
In thename
theset & get
Methods:And you can see, why in the bottomset & get
Does a method have parameters? That’s what we always sayHidden parameters.
What is OBJC_IVAR_$_Person$_name? Extern “C” unsigned long OBJC_IVAR_$_Person$_name; The value and assignment here are both operations on the memory address, take the first address of the Person object, offset it to the name location, and then evaluate or assign.
2 Union bitfield
2.1 Union
First let’s look at what a union is:
union Teacher {
char *name;
int age;
double height ;
};
Copy the code
This is a combination.
- The characteristics of a union are as follows: each variable is mutually exclusive, that is, only one variable can be assigned; Its advantage is that the memory usage is more delicate and flexible, but also saves the memory space.
- Structs are characterized by the coexistence of variables, that is, multiple variables can be assigned at the same time; Its disadvantage is more extensive use of memory space, regardless of use, memory allocation.
So let’s look at it in codeA consortium:
2.2 a domain
For example, we have the following structure:
struct Car1 {
BOOL front;
BOOL back;
BOOL left;
BOOL right;
};
Copy the code
The size of Car1 is 4 bytes:
Here’s the problem, we don’t actually need that much memory, we only need one byte to fully express Car1. To achieve this effect, we only need to specify the number of bits occupied by the member variable when the structure is defined. This is the Bit field:
struct Car2 {
BOOL front;
BOOL back : 1;
BOOL left : 6;
BOOL right: 4;
};
Copy the code
: used to limit the number of digits occupied by a member variable; There is no limit to front, which is estimated to be 1 Byte based on the type. Back, left, and right are limited by the number following:. The length cannot be calculated according to the data type, which occupies 1(Bit), 6(Bit), and 4(Bit) memory respectively.
3 Association between ISA and Class
3.1 isa_t
We were analyzingalloc
Process the time in_class_createInstanceFromZone
Contains this code:As in:
inline void objc_object::initIsa(Class cls, bool nonpointer, UNUSED_WITHOUT_INDEXED_ISA_AND_DTOR_BIT bool hasCxxDtor) { ASSERT(! isTaggedPointer()); isa_t newisa(0); if (! nonpointer) { newisa.setClass(cls, this); } else { ASSERT(! DisableNonpointerIsa); ASSERT(! cls->instancesRequireRawIsa()); #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 # if ISA_HAS_CXX_DTOR_BIT newisa.has_cxx_dtor = hasCxxDtor; # endif newisa.setClass(cls, this); #endif newisa.extra_rc = 1; } // 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 instantiation isa = newisa; }Copy the code
We can see from the above code:
isa = newisa;
;newisa
isisa_t
Type.
Then we follow up ISA_T, and it can be found that ISA_T isa consortium:
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
uintptr_t bits;
private:
// Accessing the class requires custom ptrauth operations, so
// force clients to go through setClass/getClass by making this
// private.
Class cls;
public:
#if defined(ISA_BITFIELD)
struct {
ISA_BITFIELD; // defined in isa.h
};
bool isDeallocating() {
return extra_rc == 0 && has_sidetable_rc == 0;
}
void setDeallocating() {
extra_rc = 0;
has_sidetable_rc = 0;
}
#endif
void setClass(Class cls, objc_object *obj);
Class getClass(bool authenticated);
Class getDecodedClass(bool authenticated);
};
Copy the code
Person *p = [[Person alloc] init], where p is also called pointer; Pointers have 8 bytes — 8 * 8 = 64 bits, which would be wasted if only one pointer address was stored.
Since each class has an isa, there isa store of class-related information in isa, such as whether it is being freed, reference counts, weak, associated objects, destructors, and so on.
Isa_t isa union, so we need to identify the memory stored in it. In general, we need to look at the bitfield, so we find this in ISA_BITFIELD:
This is aThe macro, its meaning is as follows (Note: There are two different circumstancesThe macro, will be introduced below) :
The variable name | The meaning of value |
---|---|
nonpointer |
Is it true or notisa Pointer Pointer optimization is enabled.0 : pureisa Pointer;1 : Not only class object address,isa Contains class information, object reference counts, and so on. |
has_assoc |
Associated object flag bit0 : no;1 : there is. |
has_cxx_dtor |
Whether the object hasC++ orObjc If there is a destructor, the destructor logic needs to be set; if there is no destructor, the object can be freed faster. |
shiftcls |
Store the value of a class pointer. Turn on pointer optimization in case ofarm64 In the schema, 33 bits are used to store class Pointers. |
magic |
Used by the debugger to determine if the current object is a real object or if there is no space to initialize it. |
weakly_referenced |
A variable used to indicate whether an object is or will be pointed to an ARC. Objects without weak references can be released faster. |
deallocating |
Flags whether the object is freeing memory. |
has_sidetable_rc |
When the object reference count is greater than 10, the variable is used to store the carry. |
extra_rc |
Represents the reference-count value of the object, which is actually the reference-count value minus 1. For example, if the reference count of the object is 10, thenextra_rc 9; If the reference count is greater than 10, the above is requiredhas_sidetable_rc . |
X86_64 and ARM64, ISA_BITFIELD is compared as follows:
3.2 Relationship between ISA and Class Addresses
How do we get through?isa
Pointer to the corresponding class address, notice there’s one up thereISA_MASK
Macros, we useisa & ISA_MASK
You can get the class address as follows:
Why? Because Apple doesn’t want us to get the corresponding value directly, that is, it doesn’t want the value to be explicitly exposed. So I added a mask to match that.
3.3 initIsa
In initIsa, if nonpointer == 0(pure ISA pointer), isa = ISA_t ((uintptr_t) CLS); ; Otherwise, a series of bit assignments (bit-field assignments) are performed:
4 Isa Class assignment reverse process
Here we use bit operations to get the Class address we want. Let’s first review x86_64’s ISA_BITFIELD from above:
Shifcls is what we’re looking for; Shifcls has 3 bits on the right and 17 bits on the left. So we move three to the right, then 20 to the left, and finally 17 to the right to get Shifcls.
Specific operations are as follows: