The object nature
@interface LGPerson : NSObject
@property (nonatomic, copy) NSString *name;
@end
@implementation LGPerson
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"Hello, World!");
}
return 0;
}
Copy the code
The LGPerson class in main. CPP is compiled to LGPerson_IMPL:
//main.cpp
The LGPerson class is compiled to LGPerson_IMPL
struct LGPerson_IMPL {
struct NSObject_IMPL NSObject_IVARS;// equivalent to Class isa;
NSString *_name;
};
// The underlying NSObject is compiled to NSObject_IMPL
struct NSObject_IMPL {
Class isa;
};
The definition of / / NSObject
@interface NSObject <NSObject> {
Class isa OBJC_ISA_AVAILABILITY;
}
Copy the code
LGPerson
Class is compiled toLGPerson_IMPL
The first propertyNSObject_IVARS
Equivalent toClass isa
Is defined as the parent structure;- Structs can be inherited in c++, but in
c
Can be implemented in this wayPseudo inheritance
. meansLGPerson
haveNSObject
In theAll member variables
. - An OC object is essentially a structure.
- Structs can be inherited in c++, but in
whyisa
Is of typeClass
?
-
InitInstanceIsa (int, int, int, int, int, int, int, int, int, int, int, int, int);
-
In NSObject, ISA is defined as Class. The fundamental reason is that ISA sends back Class information. In order to make developers more clear, isa needs to perform a type cast when it returns, similar to the strong cast of AS in Swift. Isa’s strong transformation in the source code is as follows:
The get and set methods of name
// Get method of name
static NSString * _I_LGPerson_name(LGPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_name)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
// Set method of name
static void _I_LGPerson_setName_(LGPerson * self, SEL _cmd, NSString *name) {
objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct LGPerson, _name), (id)name, 0.1);
}
Copy the code
set
The implementation of a method depends on the runtimeobjc_setProperty
objc_setProperty
The source code is as follows:Enter thereallySetProperty
Source code implementation, the principle of its method isNew value retain, old value release
forobjc_setProperty
The underlying source code exploration summary
-
The purpose of the objc_setProperty method is to associate the upper set method with the lower set method, which is essentially an interface. Interface isolation objc_setProperty ADAPTS to a unified entry, and CMD differentiates between sets.
-
The reason for this design is that there are many upper set methods. If you call the lower set method directly, there will be a lot of temporary variables. When you want to find an SEL, it will be very troublesome
-
Based on the above reasons, apple has adopted the adapter design pattern (the underlying interface adapter for the client required interfaces), provides an interface that is for the use of the upper set method, internal calls at the bottom of the set method, make its are not affected each other, so no matter how the upper and the lower is constant, change can affect the upper or lower level, It is mainly used to isolate the upper and lower interfaces.
How CLS relates to classes
The first two of the three core methods in Alloc are analyzed in the source code analysis of object principles – Alloc and Init. Today we explore how initInstanceIsa associates CLS with ISA.
Before we do that, we need to know what isa union and why is isa’s type isa_t defined as a union
Union
The union is composed of different data types, but its variables are mutually exclusive, and all the members occupy a segment of memory. Moreover, the common body adopts the memory overwrite technology, which can only store the value of one member at a time. If a value is assigned to a new member, the value of the original member will be overwritten. Its memory footprint is equal to that of the largest member.
The type of ISA is ISA_t
union isa_t { / / a consortium
isa_t() { }
isa_t(uintptr_t value) : bits(value){}// CLS and bits are mutually exclusive
Class cls;
uintptr_t bits;
#if defined(ISA_BITFIELD)
struct {
ISA_BITFIELD; // defined in isa.h
};
#endif
};
Copy the code
The reason for the use of union for the ISA_t type is also based on memory optimization, which is implemented in the ISA pointer through the principle of char + bitfield (that is, each bit in binary can represent different information). Typically, isa Pointers take up 8 bytes of memory, or 64 bits, which is enough to store a lot of information and can result in significant memory savings to improve performance. It can be seen from the definition of ISA_t:
-
It provides two members, CLS and bits, which are mutually exclusive, as defined by the union, meaning that there are two ways to initialize isa Pointers
- through
cls
Initialization,Bits No default value
- through
bits
Initialization,CLS has default values
- through
-
Also provided is a structure defined by
A domain
, used to store class information and other information, members of a structureISA_BITFIELD
This is aThe macro
Definition, there are two versions__arm64__
(on ios mobile) and__x86_64__
(for macOS), here are some of their macro definitions, as shown below:nonpointer
There are two values, representing custom classes, etc., from the first0
Bit start, occupy1
position0
:Pure isa pointer
1
: not onlyClass object address
Isa containsThe class information
And the object’sReference counting
Etc.
has_assoc
saidAssociated object flag
, from the first1
Bit start, occupy1
position0
:There is no relationship
object1
:A link between
object
has_cxx_dtor
Indicates whether the object has C++/OCDestructor machine
(similar to thedealloc
),1
position- if
There are
Destructor, is requiredDo the destructor
logic - if
There is no
, can be fasterThe release of
object
- if
shiftcls
saidStore the value of a pointer to a class
(The address of the class), that is, the class informationarm64
A majority of the33
Bit to turn on pointer optimization in case of arm64 architecture33
Bits are used to store class Pointersx86_64
A majority of the44
position
magic
Used by the debugger to determine whether the current object isThe real object
orThere is no space for initialization
, accounting for6
positionweakly_refrenced
Refers to the objectWhether or not it is directed
orWeak variable that used to point to an ARC
- Objects without weak references can be released faster
deallocating
The flag object isIs it being released?
memoryhas_sidetable_rc
Represents when an objectThe reference count is greater than 10
, is neededBorrow the variable to store the carry
extra_rc
(Additional reference count), indicating thatThe reference to the object counts the value
, is actually the reference count value minus 1- If the object’s
The reference count is 10
, thenextra_rc
9 (this is for example only), which is actually true for the iPhoneextra_rc
It uses 19 bits to store reference counts
- If the object’s
For two different platforms, theisa
As shown in the figure:
The source code to explore
- through
alloc --> _objc_rootAlloc --> callAlloc --> _objc_rootAllocWithZone --> _class_createInstanceFromZone
Method path, found toinitInstanceIsa
And into its principle of implementation
inline void
objc_object: :initInstanceIsa(Class cls, bool hasCxxDtor){ ASSERT(! cls->instancesRequireRawIsa()); ASSERT(hasCxxDtor == cls->hasCxxDtor());// Initialize isa
initIsa(cls, true, hasCxxDtor);
}
Copy the code
-
Enter the source implementation of the initIsa method, primarily to initialize the ISA pointer
The logic of this method is divided into two parts:
- through
cls
Initialize theisa
- through
bits
Initialize theisa
- through
Verifying isa pointer bit fields (0-64)
From the 0-64 bit fields mentioned above, you can prove here that isa pointer has these bit fields in the initIsa method (currently on macOS, so x86_64 is used).
-
Init with the LGPerson breakpoint in main –> initInstanceIsa –> initIsa –> go to ISA in else
-
Run the LLDB command p newisa to obtain details about newisa
-
To continue, go to newisa.bits = ISA_MAGIC_VALUE; On the next line, assign a value to the bits member of ISA and re-execute the LLDB command p newisa. The result is as follows
-
By comparing the information with the previous newISA, there are some changes in the ISA pointer, as shown in the figure below
-
Magic is 59 because the ISA pointer address is converted into binary, 6 bits are read from 47 (because there are 4 bit fields in front, 47 bits are occupied, and the address starts from 0), and then converted into decimal, as shown in the figure below
Isa associates Pointers and classes
The principle of CLS association with ISA is that the shiftCls bit field in ISA pointer stores the class information, where initInstanceIsa process is to associate the Calloc pointer with the current CLS class. There are the following verification methods:
-
Shiftcls = (uintptr_t) CLS >> 3; validation
-
[Method 2] Verify the isa pointer address and the value of ISA_MSAK &
-
[Method 3] Verify this by using the object_getClass runtime method
-
[Mode 4] Verify by bit operation
Method 1: Use the initIsa method
Uintptr_t CLS >> 3; Previous step, where Shiftcls stores the value information for the current class
-
When you look at CLS, it’s the LGPerson class
-
The logic of shiftcls assignment is to shift LGPerson three bits to the right after encoding it
-
Execute the LLDB command p (uintptr_t) CLS, the result is (uintptr_t) $2 = 4294975720, move it three places right in either of the following ways to get 536871965 and store it in shiftcls of Newisa
p (uintptr_t)cls >> 3
- Through the results of the previous step
$2
Run the LLDB commandp $2 >> 3
-
Continue the program to isa = newisa; P newisa is executed
There are two changes in the bit field of bits compared to the result of the bits assignment
cls
由The default value
And turned intoLGPerson
That will beIsa and CLS are perfectly correlated
shiftcls
by0
Turned out to be536871965
soisa
Through theInitialize the
After theMembers of the
theProcess of value change
, as shown in the figure below: Why do I need a strong type when SHIFtcls is assigned?
Since the memory store cannot store strings, machine code can only recognize 0 and 1, so it needs to convert them to uintptr_t data type so that the class information stored in ShiftCLs can be understood by machine code, where Uintptr_t is unsigned long
Why do I need to move 3 to the right?
This is mainly because Shiftcls is in the middle of the ISA pointer address, and there are three bit fields in front of it. In order not to affect the data in the first three bit fields, it needs to be zeroed by right shift.
Method 2: Use ISA & ISA_MSAK
-
After method 1, go back to _class_createInstanceFromZone. At this point, CLS is associated with ISA. Execute Po objc
-
Execute x/4gx obj to obtain the address of isa pointer 0x001D8001000020e9
-
Isa pointer address & ISA_MASK (in macOS, defined using macros in X86_64), Po 0x001D8001000020e9&0x00007ffffffFF8ull, resulting in LGPerson
arm64
, the value defined by the ISA_MASK macro is0x0000000ffffffff8ULL
x86_64
, the value defined by the ISA_MASK macro is0x00007ffffffffff8ULL
Method 3: Use object_getClass
You can also verify isa’s association with a class by viewing object_getClass’s source code implementation. There are several steps:
- #import
- through
runtime
API, namelyobject_getClass
Function to get class information.
object_getClass(<#id _Nullable obj#>)
Copy the code
-
View the object_getClass function source code implementation
-
Click on the underlying implementation of object_getClass
-
Enter getIsa source code implementation
-
Click ISA() to access the source code, and you can see that if it’s indexed, the if flow is executed, and else flow is executed instead
- in
else
In the process, getisa
thebits
This place, and then& ISA_MASK
, which is consistent with the principle in mode two,Get the current class information
- You can do it from here
CLS and ISA have been perfectly correlated
- in
Mode 4: Through bit operation
-
Back to the _class_createInstanceFromZone method. X /4gx obj is used to get the obj storage information. The current class information is stored in isa pointer, and shiftcls in ISA is 44 bits (because of macOS environment).
-
If you want to read the 44-bit information in the middle, you need to go through the bit operation, erase the three bits on the right and the part on the left except the 44-bit, and its relative position is unchanged. Its bit operation process is shown in the figure, where Shiftcls is the class information to be read
-
Move the ISA address right by 3 bits: P /x 0x001D8001000020E9 >> 3 to get 0x0003B0002000041D
-
Shift the resulting 0x0003B0002000041D 20 bits to the left: p/x 0x0003B0002000041D << 20, resulting in 0x0002000041D00000
- Why is it
The left 20
Who? Because the firstright
Move the3
Bit is the same thing as going to the rightIt's offset by 3
And theOn the left
Need to beMaLing
The figures are17
Bit, so we have to move it20
position
- Why is it
-
Move the resulting 0x0002000041D00000 17 bits right: p/x 0x0002000041D00000 >> 17 to get the new 0x00000001000020E8
-
-
P /x CLS also produces 0x00000001000020E8, so it can be proved that CLS is associated with ISA
The article lists
- IOS Articles List
Thanks for the article reference
- IOS – Underlying Principles 07: How ISA is associated with classes