《iOS底层原理文章汇总》
类分析
1.获取对象的isa,得到类信息
(lldb) x/4gx person
0x1007041a0: 0x001d8001000021a9 0x0000000000000000
0x1007041b0: 0x00007fff8a1bc860 0x000000010063ec10
(lldb) p/x 0x001d8001000021a9 & 0x00007ffffffffff8ULL
(unsigned long long) $1 = 0x00000001000021a8
(lldb) po 0x00000001000021a8
DCPerson
I. 查看类的地址,类有关于类的内存分布
1.通过打印isa
(lldb) x 0x00000001000021a8
0x1000021a8: 80 21 00 00 01 00 00 00 40 61 33 00 01 00 00 00 .!......@a3.....
0x1000021b8: d0 03 33 00 01 00 00 00 00 00 00 00 10 80 00 00 ..3.............
2.通过打印类.class
(lldb) x DCPerson.class
0x1000021a8: 80 21 00 00 01 00 00 00 40 61 33 00 01 00 00 00 .!......@a3.....
0x1000021b8: d0 03 33 00 01 00 00 00 00 00 00 00 10 80 00 00 ..3.............
3.通过runtime底层api打印object_getClass(对象)
(lldb) x object_getClass(person)
0x1000021a8: 80 21 00 00 01 00 00 00 40 61 33 00 01 00 00 00 .!......@a3.....
0x1000021b8: d0 03 33 00 01 00 00 00 00 00 00 00 10 80 00 00 ..3.............
II. 查看isa的关于类的内存地址的内存分布,与上MASK和不与上MASK的值是一样的,最后得到的首地址的值还为isa指针,指向类
(lldb) x/4gx 0x00000001000021a8
0x1000021a8: 0x0000000100002180 0x0000000100336140
0x1000021b8: 0x00000001003303d0 0x0000801000000000
(lldb) po 0x0000000100002180
DCPerson
(lldb) p/x 0x0000000100002180 & 0x00007ffffffffff8ULL
(unsigned long long) $6 = 0x0000000100002180
(lldb) po 0x0000000100002180
DCPerson
两个内存地址的值(0x00000001000021a8和0x0000000100002180)都是DCPerson???这是为什么呢,到底哪一个是DCPerson呢???
于是就有元类的概念--系统提供元类,对象-isa-类(对象)->元类,接下来会有方法,协议,属性的归属,元类的定义和创建都是由编译器自动完成,类方法存储在元类里面
- 1.查看元类的内存信息
(lldb) x/4gx DCPerson.class
0x1000021a8: 0x0000000100002180 0x0000000100336140
0x1000021b8: 0x00000001003303d0 0x0000801000000000
(lldb) p/x 0x0000000100002180 & 0x00007ffffffffff8ULL
(unsigned long long) $9 = 0x0000000100002180
(lldb) x/4gx 0x0000000100002180
0x100002180: 0x00000001003360f0 0x00000001003360f0
0x100002190: 0x0000000100706a90 0x0004e03100000007
- 2.再往下查看根元类的内存信息
(lldb) p/x 0x00000001003360f0 & 0x00007ffffffffff8ULL
(unsigned long long) $10 = 0x00000001003360f0
(lldb) x/4gx 0x00000001003360f0
0x1003360f0: 0x00000001003360f0 0x0000000100336140
0x100336100: 0x0000000100782b30 0x0004e03100000007
(lldb) po 0x00000001003360f0
NSObject
和本身的系统的NSObject.class不是同一个
(lldb) po 0x00000001003360f0
NSObject
(lldb) po NSObject.class
NSObject
(lldb) p/x NSObject.class
(Class) $13 = 0x0000000100336140 NSObject
面试题:NSObject类存在几份,类的信息在内存中永远只存在一份
isa 对象 -> 类(DCPerson) -> 元类(DCPerson) -> NSObject(根元类) -> NSObject(根元类)
- 3.分析类对象内存存在个数
void lgTestClassNum(){
Class class1 = [DCPerson class];
Class class2 = [DCPerson alloc].class;
Class class3 = object_getClass([DCPerson alloc]);
Class class4 = [DCPerson alloc].class;
NSLog(@"\n%p-\n%p-\n%p-\n%p",class1,class2,class3,class4);
}
0x1000020f0-
0x1000020f0-
0x1000020f0-
0x1000020f0
- 4.上面得出的不是NSObject的类,而是NSObject的元类,也叫根元类,地址0x00000001003360f0是一样的。验证如下:
(lldb) po 0x00000001003360f0
NSObject
(lldb) po NSObject.class
NSObject
(lldb) p/x NSObject.class
(Class) $13 = 0x0000000100336140 NSObject
(lldb) x/4gx 0x0000000100336140
0x100336140: 0x00000001003360f0 0x0000000000000000
0x100336150: 0x000000010067fae0 0x0001801000000003
(lldb) p/x 0x00000001003360f0 & 0x00007ffffffffff8ULL
(unsigned long long) $14 = 0x00000001003360f0
(lldb) po 0x00000001003360f0
NSObject
- 5.继续往下探究:查看根元类的isa指向的类的内存信息
(lldb) x/4gx DCPerson.class
0x1000021a8: 0x0000000100002180 0x0000000100336140
0x1000021b8: 0x00000001003303d0 0x0000801000000000
(lldb) p/x 0x0000000100002180 & 0x00007ffffffffff8ULL
(unsigned long long) $9 = 0x0000000100002180
(lldb) x/4gx 0x0000000100002180
0x100002180: 0x00000001003360f0 0x00000001003360f0
0x100002190: 0x0000000100706a90 0x0004e03100000007
(lldb) p/x 0x00000001003360f0 & 0x00007ffffffffff8ULL
(unsigned long long) $10 = 0x00000001003360f0
(lldb) x/4gx 0x00000001003360f0
0x1003360f0: 0x00000001003360f0 0x0000000100336140
0x100336100: 0x0000000100782b30 0x0004e03100000007
(lldb) po 0x00000001003360f0
NSObject
(lldb) po NSObject.class
NSObject
(lldb) p/x NSObject.class
(Class) $13 = 0x0000000100336140 NSObject
(lldb) x/4gx 0x0000000100336140
0x100336140: 0x00000001003360f0 0x0000000000000000
0x100336150: 0x000000010067fae0 0x0001801000000003
(lldb) p/x 0x00000001003360f0 & 0x00007ffffffffff8ULL
(unsigned long long) $14 = 0x00000001003360f0
(lldb) po 0x00000001003360f0
NSObject
(lldb) x/4gx 0x00000001003360f0
0x1003360f0: 0x00000001003360f0 0x0000000100336140
0x100336100: 0x0000000100782b30 0x0005e03100000007
(lldb) p/x 0x00000001003360f0 & 0x00007ffffffffff8ULL
(unsigned long long) $16 = 0x00000001003360f0
(lldb) po 0x00000001003360f0
NSObject
(lldb) x/4gx 0x00000001003360f0
0x1003360f0: 0x00000001003360f0 0x0000000100336140
0x100336100: 0x0000000100782b30 0x0005e03100000007
(lldb) p/x 0x00000001003360f0 & 0x00007ffffffffff8ULL
(unsigned long long) $18 = 0x00000001003360f0
(lldb) po 0x00000001003360f0
NSObject
isa指向关系
只有NSObject少了一步元类的指向
void lgTestNSObject(){
//NSObject实例对象
NSObject *object1 = [NSObject alloc];
//NSObject类
Class class = object_getClass(object1);
//NSObject元类
Class metaClass = object_getClass(class);
//NSObject根元类
Class rootMetaClass = object_getClass(metaClass);
//NSObject根根元类
Class rootRootMetaClass = object_getClass(rootMetaClass);
NSLog(@"\n%p 实例对象\n%p 类\n%p 元类\n%p 根元类\n%p 根根元类",object1,class,metaClass,rootMetaClass,rootRootMetaClass);;
}
0x100479120 实例对象
0x7fff92977118 类
0x7fff929770f0 元类
0x7fff929770f0 根元类
0x7fff929770f0 根根元类
6.实例对象之间根本没有任何关系,即没有继承关系,继承关系来自于类,只有类才有继承关系,根元类指向NSObject
DCTeacher(儿子) --> DCPerson(父亲) --> NSObject --> (isa) nil 0-1
teacher继承于person,查看person和teacher的实例对象,类,元类,根元类的内存地址
void lgInherit(){
//DCPerson实例对象
DCPerson *person = [DCPerson alloc];
//DCPerson类
Class personClass = object_getClass(person);
//DCPerson元类
Class personMetaClass = object_getClass(personClass);
//DCPerson根元类
Class personRootMetaClass = object_getClass(personMetaClass);
//DCPerson根根元类
Class personRootRootMetaClass = object_getClass(personRootMetaClass);
NSLog(@"\n%p 实例对象\n%p 类\n%p 元类\n%p 根元类\n%p 根根元类\n",person,personClass,personMetaClass,personRootMetaClass,personRootRootMetaClass);
//DCTeacher实例对象
DCPerson *teacher = [DCTeacher alloc];
//DCTeacher类
Class teacherClass = object_getClass(teacher);
//DCTeacher元类
Class teacherMetaClass = object_getClass(teacherClass);
//DCTeacher根元类
Class teacherRootMetaClass = object_getClass(teacherMetaClass);
//DCTeacher根根元类
Class teacherRootRootMetaClass = object_getClass(teacherRootMetaClass);
NSLog(@"\n%p 实例对象\n%p 类\n%p 元类\n%p 根元类\n%p 根根元类",teacher,teacherClass,teacherMetaClass,teacherRootMetaClass,teacherRootRootMetaClass);
}
2020-10-04 15:33:51.316625+0800 iOS-isa分析[46409:2533244]
0x10040aea0 实例对象
0x100002198 类
0x100002170 元类
0x7fff929770f0 根元类
0x7fff929770f0 根根元类
2020-10-04 15:33:51.316711+0800 iOS-isa分析[46409:2533244]
0x100704c40 实例对象
0x1000021e8 类
0x1000021c0 元类
0x7fff929770f0 根元类
0x7fff929770f0 根根元类
通过clang编译成C++文件发现,对象和类的底层都是结构体,objc_object(根对象) vs object_class(Class) (NSObject) (isa)
NSObject类 VS 对象: struct objc_class:objc_object
对象 + 类 + 元类 都有isa,objc_object对象的关系,NSObject(OC),objc_object(C/C++)结构体,
struct objc_class *Class定义一个Class()
类的内存分布(p/x--p为打印,x为以十六进制打印当前内存信息)
- 1.查看类的内存信息
(lldb) x/4gx DCPerson.class
0x1000021a8: 0x0000000100002180 0x0000000100336140
0x1000021b8: 0x00000001003303d0 0x0000801000000000
(lldb) po 0x0000000100336140
NSObject
(lldb) p/x NSObject.class
(Class) $23 = 0x0000000100336140 NSObject
(lldb) 父类为NSObject
- 2.查看类信息的源码
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() const {
return bits.data();
}
void setData(class_rw_t *newData) {
bits.setData(newData);
}
void setInfo(uint32_t set) {
ASSERT(isFuture() || isRealized());
data()->setFlags(set);
}
void clearInfo(uint32_t clear) {
ASSERT(isFuture() || isRealized());
data()->clearFlags(clear);
}
}
类的methodlist,propertylist,ivarlist在哪儿???
C语言中的内存平移
- 1.通过内存地址平移获取数组中的值
//普通指针
int a = 10;
int b = 10;
DCNSLog(@"%d -- %p",a,&a);
DCNSLog(@"%d -- %p",b,&b);
//对象
DCPerson *p1 = [DCPerson alloc];
DCPerson *p2 = [DCPerson alloc];
DCNSLog(@"%@ -- %p",p1,&p1);
DCNSLog(@"%@ -- %p",p2,&p2);
//数组指针
int c[4] = {1,2,3,4};
int * p = c;
for (int i = 0; i<4; i++) {
int value = c[i];
int v = *(p+i);
DCNSLog(@"%d",value);
DCNSLog(@"%d",v);
}
NSLog(@"指针-内存偏移");
//输出
10 -- 0x7ffeefbff42c
10 -- 0x7ffeefbff428
-- 0x7ffeefbff420
-- 0x7ffeefbff418
0x7ffeefbff440,0x7ffeefbff440,0x7ffeefbff440,0x7ffeefbff444,0x7ffeefbff448,0x7ffeefbff44c
1
1
2
2
3
3
4
4
2020-10-04 19:06:42.214800+0800 iOS-内存偏移[59476:2804871] 指针-内存偏移
DCPerson.class地址 - 平移 拿到所有的值
怎么获取objc_class中的cache和bits的值呢???
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() const {
return bits.data();
}
void setData(class_rw_t *newData) {
bits.setData(newData);
}
void setInfo(uint32_t set) {
ASSERT(isFuture() || isRealized());
data()->setFlags(set);
}
}
- 1.如果要知道bits的值,得先知道bits结构体属性ISA指针的大小(8字节),superclass的大小(8字节),cache的大小(未知),进行平移
I 查看cache的大小
struct cache_t {
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
explicit_atomic _buckets;
explicit_atomic _mask;
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
explicit_atomic _maskAndBuckets;
mask_t _mask_unused;
// How much the mask is shifted by.
static constexpr uintptr_t maskShift = 48;
// Additional bits after the mask which must be zero. msgSend
// takes advantage of these additional bits to construct the value
// `mask << 4` from `_maskAndBuckets` in a single instruction.
static constexpr uintptr_t maskZeroBits = 4;
// The largest mask value we can store.
static constexpr uintptr_t maxMask = ((uintptr_t)1 << (64 - maskShift)) - 1;
// The mask applied to `_maskAndBuckets` to retrieve the buckets pointer.
static constexpr uintptr_t bucketsMask = ((uintptr_t)1 << (maskShift - maskZeroBits)) - 1;
// Ensure we have enough bits for the buckets pointer.
static_assert(bucketsMask >= MACH_VM_MAX_ADDRESS, "Bucket field doesn't have enough bits for arbitrary pointers.");
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
// _maskAndBuckets stores the mask shift in the low 4 bits, and
// the buckets pointer in the remainder of the value. The mask
// shift is the value where (0xffff >> shift) produces the correct
// mask. This is equal to 16 - log2(cache_size).
explicit_atomic _maskAndBuckets;
mask_t _mask_unused;
static constexpr uintptr_t maskBits = 4;
static constexpr uintptr_t maskMask = (1 << maskBits) - 1;
static constexpr uintptr_t bucketsMask = ~maskMask;
#else
#error Unknown cache mask storage type.
#endif
#if __LP64__
uint16_t _flags;
#endif
uint16_t _occupied;
}
explicit_atomic
explicit_atomic
uint16_t _flags; typedef unsigned short uint16_t;2字节
uint16_t _occupied; 2字节
综上cache的大小8+4+2+2=16字节
要获取bits首地址需要平移ISA(8字节)+superclass(8字节)+cache(16) = 32字节,具体为首地址0x1000021b0平移32位得到0x1000021d0
获取class_data_bits_t bits的值:打印出firstSubclass为DCTeacher
DCPerson was compiled with optimization - stepping may behave oddly; variables may not be available.
(lldb) x/4gx DCPerson.class
0x1000021b0: 0x0000000100002188 0x0000000100336140
0x1000021c0: 0x00000001003303d0 0x0000801000000000
(lldb) p (class_data_bits_t *)0x1000021d0
(class_data_bits_t *) $1 = 0x00000001000021d0
(lldb) p $1->data()
(class_rw_t *) $2 = 0x0000000101928700
(lldb) p *$2
(class_rw_t) $3 = {
flags = 2148007936
witness = 0
ro_or_rw_ext = {
std::__1::atomic = 4294975624
}
firstSubclass = DCTeacher
nextSiblingClass = NSUUID
}
往里探究ro_or_rw_ext
(lldb) p $3.ro_or_rw_ext
(explicit_atomic) $4 = {
std::__1::atomic = 4294975624
}
- 1.查看class_rw_t源码
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint16_t witness;
#if SUPPORT_INDEXED_ISA
uint16_t index;
#endif
explicit_atomic ro_or_rw_ext;
Class firstSubclass;
Class nextSiblingClass;
private:
using ro_or_rw_ext_t = objc::PointerUnion;
const ro_or_rw_ext_t get_ro_or_rwe() const {
return ro_or_rw_ext_t{ro_or_rw_ext};
}
void set_ro_or_rwe(const class_ro_t *ro) {
ro_or_rw_ext_t{ro, &ro_or_rw_ext}.storeAt(ro_or_rw_ext, memory_order_relaxed);
}
void set_ro_or_rwe(class_rw_ext_t *rwe, const class_ro_t *ro) {
// the release barrier is so that the class_rw_ext_t::ro initialization
// is visible to lockless readers
rwe->ro = ro;
ro_or_rw_ext_t{rwe, &ro_or_rw_ext}.storeAt(ro_or_rw_ext, memory_order_release);
}
class_rw_ext_t *extAlloc(const class_ro_t *ro, bool deep = false);
public:
void setFlags(uint32_t set)
{
__c11_atomic_fetch_or((_Atomic(uint32_t) *)&flags, set, __ATOMIC_RELAXED);
}
void clearFlags(uint32_t clear)
{
__c11_atomic_fetch_and((_Atomic(uint32_t) *)&flags, ~clear, __ATOMIC_RELAXED);
}
// set and clear must not overlap
void changeFlags(uint32_t set, uint32_t clear)
{
ASSERT((set & clear) == 0);
uint32_t oldf, newf;
do {
oldf = flags;
newf = (oldf | set) & ~clear;
} while (!OSAtomicCompareAndSwap32Barrier(oldf, newf, (volatile int32_t *)&flags));
}
class_rw_ext_t *ext() const {
return get_ro_or_rwe().dyn_cast(&ro_or_rw_ext);
}
class_rw_ext_t *extAllocIfNeeded() {
auto v = get_ro_or_rwe();
if (fastpath(v.is())) {
return v.get(&ro_or_rw_ext);
} else {
return extAlloc(v.get(&ro_or_rw_ext));
}
}
class_rw_ext_t *deepCopy(const class_ro_t *ro) {
return extAlloc(ro, true);
}
const class_ro_t *ro() const {
auto v = get_ro_or_rwe();
if (slowpath(v.is())) {
return v.get(&ro_or_rw_ext)->ro;
}
return v.get(&ro_or_rw_ext);
}
void set_ro(const class_ro_t *ro) {
auto v = get_ro_or_rwe();
if (v.is()) {
v.get(&ro_or_rw_ext)->ro = ro;
} else {
set_ro_or_rwe(ro);
}
}
const method_array_t methods() const {
auto v = get_ro_or_rwe();
if (v.is()) {
return v.get(&ro_or_rw_ext)->methods;
} else {
return method_array_t{v.get(&ro_or_rw_ext)->baseMethods()};
}
}
const property_array_t properties() const {
auto v = get_ro_or_rwe();
if (v.is()) {
return v.get(&ro_or_rw_ext)->properties;
} else {
return property_array_t{v.get(&ro_or_rw_ext)->baseProperties};
}
}
const protocol_array_t protocols() const {
auto v = get_ro_or_rwe();
if (v.is()) {
return v.get(&ro_or_rw_ext)->protocols;
} else {
return protocol_array_t{v.get(&ro_or_rw_ext)->baseProtocols};
}
}
};
类中添加一个属性一个成员变量,一个实例方法和一个类方法
@interface DCPerson : NSObject{
NSString *hobby;
}
@property(nonatomic,copy)NSString *kc_name;
-(void)sayNB;
+(void)say666;
@end
- 2.属性的值:有一个属性kc_name,和一个成员变量的hobby,目前只能打印kc_name的值
(lldb) x/4gx DCPerson.class
0x1000022c0: 0x0000000100002298 0x0000000100336140
0x1000022d0: 0x00000001003303d0 0x0000802400000000
(lldb) p (class_data_bits_t *)0x1000022e0
(class_data_bits_t *) $1 = 0x00000001000022e0
(lldb) p $1->data()
(class_rw_t *) $2 = 0x0000000100678680
(lldb) p $2->properties()
(const property_array_t) $3 = {
list_array_tt = {
= {
list = {
ptr = 0x00000001000021b8
}
arrayAndFlag = 4294975928
}
}
}
(lldb) p $3.list.ptr
(property_list_t *const) $4 = 0x00000001000021b8
(lldb) p *$4
(property_list_t) $5 = {
entsize_list_tt = (entsizeAndFlags = 16, count = 1)
}
(lldb) p $5.get(0)
(property_t) $6 = (name = "kc_name", attributes = "T@\"NSString\",C,N,V_kc_name")
(lldb) p $5.get(1)
Assertion failed: (i < count), function get, file /Users/cloud/Documents/iOS/0911/0911练习/iOS-isa指针/runtime/objc-runtime-new.h, line 479.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
count = 1为1表示仅有1个属性kc_name
- 3.方法的值:count = 4,表明有4个方法,sayNB,属性的setter和getter方法,kc_name,setKc_name:,.cxx_destruct,C++位于OC底层,默认会自动添加方法,都是实例方法,并没有类方法say666()。
(lldb) p/x DCPerson.class
(Class) $0 = 0x00000001000022c0 DCPerson
(lldb) p (class_data_bits_t *)0x00000001000022e0
(class_data_bits_t *) $1 = 0x00000001000022e0
(lldb) p $1->data()
(class_rw_t *) $2 = 0x000000010089e5f0
(lldb) p *$2
(class_rw_t) $3 = {
flags = 2148007936
witness = 0
ro_or_rw_ext = {
std::__1::atomic = 4294975680
}
firstSubclass = DCTeacher
nextSiblingClass = NSUUID
}
(lldb) p $3.methods()
(const method_array_t) $4 = {
list_array_tt = {
= {
list = {
ptr = 0x0000000100002108
}
arrayAndFlag = 4294975752
}
}
}
(lldb) p $4.list.ptr
(method_list_t *const) $5 = 0x0000000100002108
(lldb) p *$5
(method_list_t) $6 = {
entsize_list_tt = (entsizeAndFlags = 26, count = 4)
}
(lldb) p $6.get(0)
(method_t) $7 = {}
(lldb) p $7.name()
(SEL) $8 = "sayNB"
(lldb) p $7.types()
(const char *) $9 = 0x0000000100000f7c "v16@0:8"
(lldb) p $6.get(1)
(method_t) $10 = {}
(lldb) p $10.name()
(SEL) $11 = "kc_name"
(lldb) p $10.types()
(const char *) $12 = 0x0000000100000f90 "@16@0:8"
(lldb) p $6.get(2)
(method_t) $13 = {}
(lldb) p $13.name()
(SEL) $14 = "setKc_name:"
(lldb) p $13.types()
(const char *) $15 = 0x0000000100000f98 "v24@0:8@16"
(lldb) p $6.get(3)
(method_t) $16 = {}
(lldb) p $16.name()
(SEL) $17 = ".cxx_destruct"
(lldb) p $16.types()
(const char *) $18 = 0x0000000100000f7c "v16@0:8"
(lldb) p $6.get(4)
Assertion failed: (i < count), function get, file /Users/cloud/Documents/iOS/0911/0911练习/iOS-isa指针/runtime/objc-runtime-new.h, line 479.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.