preface
- IOS low-level exploration — Alloc, init, new exploration
- IOS Low-level Exploration – Memory byte alignment analysis
- IOS Low-level Exploration – The nature of objects
- IOS Low-level Exploration – Isa initialization & Pointing Analysis
Continuous pointer memory offset
An int array [] = {1, 4-trichlorobenzene}; int *b = array; NSLog(@"%p-%p-%p-%p",&array,&array[0],&array[1],&array[2]);
NSLog(@"%p-%p-%p",b,b+1,b+2);
for (int i = 0; i < 3; i++) {
int value = *(b+i);
NSLog(@"value-%d",value); } 2019-12-21 21:06:11.914923+0800 XDTest[2663:291951] 0x7ffeefbff5bc-0x7ffeefbff5c0-0x7ffeefbff5C4 2019-12-21 21:06:11.915605+0800 XDTest[2663:291951] 0x7ffeefbff5bc-0x7ffeefbff5c0-0x7ffeefbff5c4 2019-12-21 21:06:11.915673+0800 XDTest[2663:291951] value-1 2019-12-21 21:06:11.915729+0800 XDTest[2663:291951] value-2 2019-12-21 21:06:11. 915756 + 0800 XDTest [2663-291951] value - 4Copy the code
- Pointer to the
b
The address ofarray
The first address of the array.- The next contiguous memory address can be found by pointer offset.
Class structure analysis
1. Everything is an object
typedef struct objc_class *Class;
struct objc_class : objc_object{};
Copy the code
- From the source code we can know the class
Class
The essence ofobjc_class
.objc_class
Inherited fromobjc_object
Verify that everything is an object.
Q1: Objc_class is related to NSObject.
NSObject is a class, essentially objc_class.
Q2: The relationship between objC_Object and NSObject?
NSObject is of type OC, objc_Object is of type C. NSObject is a wrapper around objc_Object.
2. Class structure
struct objc_class : objc_object {
// Class ISA; //8
Class superclass; //8
cache_t cache; //16 // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
class_rw_t *data() {
returnbits.data(); }... Omit other information... };Copy the code
3. Class structure member analysis
3.1 Class ISA
An annotated member, representing inherited from the parent class, takes 8 bytes.
struct objc_object { private: isa_t isa; . Omit other information... };Copy the code
3.2 Class superclass
A pointer to the parent Class, which is itself a pointer, takes 8 bytes.
3.3 cache_t cache
As the name implies, it is an object that stores the cache and takes up 16 bytes
truct cache_t { struct bucket_t *_buckets; // Pointer takes up 8 bytes mask_t _mask; Mask_t _occupied; // Take up 4 bytes... Omit other information... };Copy the code
3.4 class_data_bits_t bits
A structure, we find that some of the relevant information of the class is not seen in the first three members, with this we can analyze, class related attributes, member variables, methods are in this structure.
4. class_data_bits_t
in-depth
By analyzing the structure of class isa, superclass, cache and other attributes, there is no relationship with our custom attributes, so we explore bits.
- We also see the following line of code in the class structure
class_rw_t *data() {
return bits.data();
}
Copy the code
The bits function can be used to retrieve the class_rw_t structure directly.
- To observe the
class_rw_t
The structure of the body
struct class_rw_t { // Be warned that Symbolication knows the layout of this structure. uint32_t flags; uint32_t version; const class_ro_t *ro; method_array_t methods; property_array_t properties; protocol_array_t protocols; . Omit other information... };Copy the code
We seem to see familiar data types associated with properties, methods, proxies, and so on. Note, however, that the properties and methods of our class do not have properties of the method_array_t or property_array_t type (which is described in a later section). It’s stored in a structure called class_ro_t, and we can see that it’s const, which means that it’s defined at compile time, and it can’t be changed later.
- To observe the
class_ro_t
The structure of the body
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endifconst uint8_t * ivarLayout; const char * name; method_list_t * baseMethodList; protocol_list_t * baseProtocols; const ivar_list_t * ivars; const uint8_t * weakIvarLayout; property_list_t *baseProperties; . Omit other information...Copy the code
- Member variable storage
const ivar_list_t * ivars
.- Attribute to deposit
property_list_t *baseProperties
.- Methods to deposit
method_list_t * baseMethodList
.
5. Attribute-method attribution exploration
5.1 Preparations
Define member variables, properties, object methods, and class methods.
@interface XDPerson : NSObject
{
NSString *otherName;
}
@property (nonatomic, copy) NSString *nickName;
- (void)sayHello;
+ (void)sayHappy;
@end
Copy the code
5.2 LLDB debugging
At the beginning of this article, we learned that we can find the memory segment in contiguity by pointer offset.
- See the class
XDPerson
Memory information of
(lldb) x/4gx XDPerson.class
0x100001318: 0x001d8001000012f1 0x0000000100aff140
0x100001328: 0x000000010203ce00 0x0000000200000003
Copy the code
- We know the information is there
objc_class
Structure of thebits
Property, found by memory address offsetbits
We’ll go straight to the memory address of0x100001318
+32 bytes =0x100001338
(lldb) p (class_data_bits_t *)0x100001338
(class_data_bits_t *) The $1 = 0x0000000100001338
Copy the code
- Looking for
class_rw_t
throughbits.data()
(lldb) p The $1->data()
(class_rw_t *) $2 = 0x000000010203cd50
Copy the code
- To view
class_rw_t
Memory data information
(lldb) p *$2
(class_rw_t) $3 = {
flags = 2148139008
version = 0
ro = 0x00000001000011f0
methods = {
list_array_tt<method_t, method_list_t> = {
= {
list = 0x0000000100001128
arrayAndFlag = 4294971688
}
}
}
properties = {
list_array_tt<property_t, property_list_t> = {
= {
list = 0x00000001000011d8
arrayAndFlag = 4294971864
}
}
}
protocols = {
list_array_tt<unsigned long, protocol_list_t> = {
= {
list = 0x0000000000000000
arrayAndFlag = 0
}
}
}
firstSubclass = nil
nextSiblingClass = NSDate
demangledName = 0x0000000000000000 <no value available>
}
Copy the code
- Check the target
ro
(lldb) p $3.ro
(const class_ro_t *) $4 = 0x00000001000011f0
Copy the code
- To view
class_ro_t
Memory data information
(lldb) p *$4
(const class_ro_t) A $5 = {
flags = 388
instanceStart = 8
instanceSize = 24
reserved = 0
ivarLayout = 0x0000000100000f80 "\x02"
name = 0x0000000100000f77 "XDPerson"
baseMethodList = 0x0000000100001128
baseProtocols = 0x0000000000000000
ivars = 0x0000000100001190
weakIvarLayout = 0x0000000000000000 <no value available>
baseProperties = 0x00000001000011d8
_swiftMetadataInitializer_NEVER_USE = {}
}
Copy the code
- To view
baseProperties
(lldb) p A $5.baseProperties
(property_list_t *const) $6 = 0x00000001000011d8
(lldb) p *$6
(property_list_t) $7 = {
entsize_list_tt<property_t, property_list_t, 0> = {
entsizeAndFlags = 16
count = 1
first = (name = "nickName", attributes = "T@"NSString",C,N,V_nickName")}}Copy the code
There is one and only one element nickName that is the same as what we defined.
- To view
ivars
(lldb) p A $5.ivars
(const ivar_list_t *const) $8 = 0x0000000100001190
(lldb) p *$8
(const ivar_list_t) $9 = {
entsize_list_tt<ivar_t, ivar_list_t, 0> = {
entsizeAndFlags = 32
count = 2
first = {
offset = 0x00000001000012e8
name = 0x0000000100000f20 "otherName"
type = 0x0000000100000fa7 "@"NSString""
alignment_raw = 3
size = 8
}
}
}
(lldb) p $9.get(0)
(ivar_t) $10 = {
offset = 0x00000001000012e8
name = 0x0000000100000f20 "otherName"
type = 0x0000000100000fa7 "@"NSString""
alignment_raw = 3
size = 8
}
(lldb) p $9.get(1)
(ivar_t) $11 = {
offset = 0x00000001000012e0
name = 0x0000000100000f2a "_nickName"
type = 0x0000000100000fa7 "@"NSString""
alignment_raw = 3
size = 8
}
Copy the code
There are two elements, one is the member variable otherName and the other is the compile-generated member variable _nickName.
- To view
baseMothedList
(lldb) p A $5.baseMethodList
(method_list_t *const) $12 = 0x0000000100001128
(lldb) p *$12
(method_list_t) $13 = {
entsize_list_tt<method_t, method_list_t, 3> = {
entsizeAndFlags = 26
count = 4
first = {
name = "sayHello"
types = 0x0000000100000f8c "v16@0:8"
imp = 0x0000000100000d70 (XDTest`-[XDPerson sayHello] at XDPerson.m:11)
}
}
}
(lldb) p $13.get(0)
(method_t) $14 = {
name = "sayHello"
types = 0x0000000100000f8c "v16@0:8"
imp = 0x0000000100000d70 (XDTest`-[XDPerson sayHello] at XDPerson.m:11)
}
(lldb) p $13.get(1)
(method_t) $15 = {
name = "nickName"
types = 0x0000000100000f94 "@ @ 0:8 16"
imp = 0x0000000100000d90 (XDTest`-[XDPerson nickName] at XDPerson.h:16)
}
(lldb) p $13.get(2)
(method_t) $16 = {
name = "setNickName:"
types = 0x0000000100000f9c "v24@0:8@16"
imp = 0x0000000100000dc0 (XDTest`-[XDPerson setNickName:] at XDPerson.h:16)
}
(lldb) p $13.get(3)
(method_t) $17 = {
name = ".cxx_destruct"
types = 0x0000000100000f8c "v16@0:8"
imp = 0x0000000100000e00 (XDTest`-[XDPerson .cxx_destruct] at XDPerson.m:10)
}
Copy the code
sayHello
Object method defined.nickName
bottom-generatedgetter
Methods.setNickName:
bottom-generatedsetter
Methods..cxx_destruct
systemc++
Destructor of.
It is also verified that the property generates ivar+getter+setter underneath.
sayHappy
Where did the class method go?
Through the analysis of this article, we can know that the class in the metaclass is also equivalent to an object. Will the class method of that class be stored in the metaclass like an object method?
. Omit debugging to find information about metaclasses... (lldb) p$25.baseMethodList
(method_list_t *const) $26 = 0x00000001000010c0
(lldb) p *$26
(method_list_t) $27 = {
entsize_list_tt<method_t, method_list_t, 3> = {
entsizeAndFlags = 26
count = 1
first = {
name = "sayHappy"
types = 0x0000000100000f8c "v16@0:8"
imp = 0x0000000100000d80 (XDTest`+[XDPerson sayHappy] at XDPerson.m:15)
}
}
}
Copy the code
The sayHappy class method is found in the metaclass.
Verify that our member traversal, properties, and methods are found in the body of class_ro_t.
In fact, THE author also went to look for the agreement, but unfortunately did not find it, I will study the agreement separately later, limited forgiveness ability, please point out the shortcomings.
Analysis of other structures
In the above analysis we saw property_t, ivar_t, method_t. Now let’s do a simple analysis.
1. property_t
struct property_t {
const char *name;
const char *attributes;
};
Copy the code
name
The name of the property;attributes
Description field;
2. ivar_t
struct ivar_t {
#if __x86_64__
// *offset was originally 64-bit on some x86_64 platforms.
// We read and write only 32 bits of it.
// Some metadata provides all 64 bits. This is harmless for unsigned
// little-endian values.
// Some code uses all 64 bits. class_addIvar() over-allocates the
// offset for their benefit.
#endif
int32_t *offset;
const char *name;
const char *type;
// alignment is sometimes -1; use alignment() instead
uint32_t alignment_raw;
uint32_t size;
uint32_t alignment() const {
if (alignment_raw == ~(uint32_t)0) return 1U << WORD_SHIFT;
return1 << alignment_raw; }};Copy the code
offset
Offset memory address;name
The name;type
Type;alignment_raw
Alignment information;size
Bytes occupied;
3. method_t
struct method_t {
SEL name;
const char *types;
MethodListIMP imp; //using MethodListIMP = IMP;
struct SortBySELAddress :
public std::binary_function<const method_t&,
const method_t&, bool>
{
bool operator() (const method_t& lhs,
const method_t& rhs)
{ returnlhs.name < rhs.name; }}; };Copy the code
name
The method name;types
Method before;imp
Method implementation;
The road of learning, forge ahead
The shortcomings can be pointed out in the comments section