探索OC对象的本质
我们知道平时编写的Objective-C
代码,底层都是C/C++
实现的,那么C/C++
又是怎么实现的呢?
- 下面看一段我们非常熟悉的
OC
代码:
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@end
@implementation Person
@end
- 通过
Clang
编译成C/C++
:
clang -rewrite-objc main.m -o main.cpp
- 编译成功后
extern "C" unsigned long OBJC_IVAR_$_Person$_name;
// Person 类对象
struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS; //isa
// 属性
NSString *_name;
};
// @property (nonatomic, copy) NSString *name;
/* @end */
// @implementation Person
// get 方法
static NSString * _I_Person_name(Person * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_Person$_name)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
// set 方法
static void _I_Person_setName_(Person * self, SEL _cmd, NSString *name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct Person, _name), (id)name, 0, 1); }
// @end
这里可以看到: 类对象
被转化成了一个结构体
- 在
OC
中进入NSObject
@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
Class isa OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}
-
C++
代码
struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSString *_name;
};
小结: 两个方面都证实NSObject
对象的本质就是一个结构体
isa流程分析
@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
Class isa OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}
- 点击
Class
进入
typedef struct objc_class *Class;
Class
是一个结构体指针.所以isa
也就是一个指针
通过这篇文章得知,对象在alloc
过程中,开辟完空间需要关联isa
isa
的创建和关联流程是怎样的呢,下面我们来以前探索
- 对象创建过程中,关联
isa
的方法
obj->initInstanceIsa(cls, hasCxxDtor);
- 点击进入
initInstanceIsa
初始化一个isa
inline void
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
ASSERT(!cls->instancesRequireRawIsa());
ASSERT(hasCxxDtor == cls->hasCxxDtor());
initIsa(cls, true, hasCxxDtor);
}
- 进入
initIsa
初始化isa
函数
inline void
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
ASSERT(!isTaggedPointer());
// 不存在,则创建isa
if (!nonpointer) {
isa = isa_t((uintptr_t)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;
// 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
newisa.has_cxx_dtor = hasCxxDtor;
// Class、Meta-Class对象的内存地址信息赋值
newisa.shiftcls = (uintptr_t)cls >> 3;
#endif
// 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;
}
}
- 进去
isa_t
isa
的结构是一个union
(联合体)
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
};
- 进入
ISA_BITFIELD
(isa
位域)
// 真机
# 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
isa
的大小为8
/字节即为64
位
isa结构分析
通过上面分析得到,isa
是一个union
结构,用位域存储信息
isa arm64 解释
-
nonpointer
:表示是否对isa
指针开启指针优化0
:纯isa指针,1
:不止是类对象地址,isa
中包含了类信息、对象的引用计数等 -
has_assoc
:关联对象标志位,0
没有,1
存在 -
has_cxx_dtor
:该对象是否有C++
或者Objc
的析构器,如果有析构函数,则需要做析构逻辑, 如果没有,则可以更快的释放对象 -
shiftcls
:存储类指针的值. 开启指针优化的情况下,在arm64
架构中有33
位用来存储类指针 -
magic
:用于调试器判断当前对象是真的对象还是没有初始化的空间 -
weakly_referenced
:对象是否被指向或者曾经指向一个ARC
的弱变量, 没有弱引用的对象可以更快释放 -
deallocating
:标志对象是否正在释放内存 -
has_sidetable_rc
:当对象引用技术大于10
时, 则需要借用该变量存储进位 -
extra_rc
:当表示该对象的引用计数值, 实际上是引用计数值减1
, 例如,如果对象的引用计数为10
,那么extra_rc
为9
,如果引用计数大于10
, 则需要使用到下面的has_sidetable_rc
通过上图我们可以得知shiftcls
存储着类的地址信息
-
下面我们用
x86_64
验证,Class
信息存储在3~46
位
与
ISA_MASK &
运算
- 查看源码可知道
#if SUPPORT_NONPOINTER_ISA
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
// 与ISA_MASK 与运算
return (Class)(isa.bits & ISA_MASK);
#endif
}
对象关系分析
总结: 类和元类是一个闭环, 实例指向类, 类指向元类, 元类指向跟元类, 跟元类指向自身, 根元类的父类是NSObject