引言
众所周知,oc是面向对象
编程,最重要的一个概念就是类
,最终OC中的类都要编译成c++
的,那么OC重的类在c++
的底层是怎么呈现的呢?
打开#import<objc/objc.h>
文件
/// An opaque type that represents an Objective-C class.
//一个不透明类型用于表示Objective-C类
typedef struct objc_class *Class;
/// Represents an instance of a class.
//表示类的实例
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
/// A pointer to an instance of a class.
typedef struct objc_object *id;
从上面代码可以看出,OC中的类(Class)
编译后在c++
中是以 struct objc_class
呈现的,我们众所周知的NSObject类
编译后在c++
中是一个 struct objc_object
,里面只有一个
成员变量,即Class
类型的变量isa
。
在这里,我们也明白了为什么oc
中的大部分对象可以用 id
接收,因为id
本身就是一个objc_object
类型的结构体指针
。
那么objc_object
和objc_class
是什么关系呢?其实我们自定义(继承于NSObject)
的类,编译成c++对应的就是objc_class
。
查看源码runtime.h
,发现结构体objc_class
如下定义,这个是老版本OBJC1_(系统低于10.15)
时候的定义
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
查看源码objc-runtime-new.h
,结构体objc_class
在新版本OBJC2_
定义如下,isa
是继承的父类objc_object
里面的isa
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
...
}
下面我们重点研究isa
,问题来了,想要研究isa
,该从哪里入手?
我们前面研究了对象alloc流程的底层源码,方法调用顺序为:alloc--->_objc_rootAlloc--->callAlloc--->_objc_rootAllocWithZone--->_class_createInstanceFromZone
.
static ALWAYS_INLINE id _class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
int construct_flags = OBJECT_CONSTRUCT_NONE,
bool cxxConstruct = true,
size_t *outAllocatedSize = nil)
{
ASSERT(cls->isRealized());
// Read class's info bits all at once for performance
bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
bool hasCxxDtor = cls->hasCxxDtor();
bool fast = cls->canAllocNonpointer();
size_t size;
size = cls->instanceSize(extraBytes); //计算对象内存大小
if (outAllocatedSize) *outAllocatedSize = size;
id obj;
if (zone) {
obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
} else {
obj = (id)calloc(1, size); //calloc 分配堆内存
}
if (slowpath(!obj)) {
if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
return _objc_callBadAllocHandler(cls);
}
return nil;
}
if (!zone && fast) {
obj->initInstanceIsa(cls, hasCxxDtor);//初始化对象的isa
} else {
// Use raw pointer isa on the assumption that they might be
// doing something weird with the zone or RR.
obj->initIsa(cls);
}
if (fastpath(!hasCxxCtor)) {
return obj;
}
construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
return object_cxxConstructFromClass(obj, cls, construct_flags);
}
代码中我们可以清晰的看到:_class_createInstanceFromZone
做了三件
事情。
- size = cls->instanceSize(extraBytes); //计算对象内存大小
- obj = (id)calloc(1, size); //calloc 分配堆内存
- obj->initIsa(cls);//初始化对象的isa
因此,initIsa
就顺利成章的成为了我们研究isa
的切入点。isa
的初始化代码如下:
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;
}
从initIsa
代码看出,该方法做了3件事。
1.初始化一个isa_t
类型的newisa
2.newisa.bits位域的初始化
3.将newisa
赋值给isa
,完成isa
的初始化工作
那么isa_t
到底是什么类型?
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);
};
可以看出,isa_t
是union(联合体)
,使用union
也是基于内存优化
,isa指针8字节,即64位,已经足够存储很多的信息了,由于union
的特性互斥性(内存共用)
,极大得节省了内存
。
提供了两个成员变量,cls
和bits
,由union的互斥性
知只能存在一种形式
初始化,那么根据什么判断呢?
不难看出,根据
nonpointer
判断用哪种方式初始化。
还提供了一个结构体定义的位域
,用于存储类信息
及其他信息,结构体的成员ISA_BITFIELD是一个宏定义。
# if __arm64__
// ARM64 simulators have a larger address space, so use the ARM64e
// scheme even when simulators build for ARM64-not-e.
# if __has_feature(ptrauth_calls) || TARGET_OS_SIMULATOR
# ...
# else
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_HAS_CXX_DTOR_BIT 1
# 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 unused : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 19
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
# endif
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# define ISA_MAGIC_MASK 0x001f800000000001ULL
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
# define ISA_HAS_CXX_DTOR_BIT 1
# 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 unused : 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
解释:
: 1;
、 : 44;
分别表示该成员占1位
、44位
下面我们来看不同位域的作用:
-
nonpointer
:是否开启指针优化
。0
:纯isa
指针只包含类对象地址
,1
:isa
中包含了类对象地址、类信息、对象的引用计数等
还得回到_class_createInstanceFromZone
if (!zone && fast) { obj->initInstanceIsa(cls, hasCxxDtor); } else { obj->initIsa(cls); }
由 obj->initInstanceIsa(cls, hasCxxDtor);
调用的initIsa
中nonpointer
传的值为true
由 obj->initIsa(cls);
调用的initIsa
中nonpointer
传的值为false
。
nonpointer
== NO
时候,走if判断,即只对newisa的class设置。
nonpointer
== YES
时候,走else判断,对newisa多项赋值。
-
has_assoc
:是否设置过关联对象,如果没有,释放时会更快。 -
has_cxx_dtor
:是否含有c++的析构函数。 -
shiftcls
:存储着Class
、Meta-Class
对象的内存地址
信息,是我们研究的重点
(arm64:33位,x86_64:44位) -
magic
:判断当前对象是否进行了初始化
。 -
weakly_referenced
:是否被弱引用指向过。 -
has_sidetable_rc
:判断是否需要用sidetable去处理引用计数(extra_rc的大小影响整个变量) -
extra_rc
:里面存储的值是引用计数器减1
这里重点提到的是shiftcls
,那么shiftcls
存的地址信息和我们的类
地址有什么关系呢?
从上面可以清晰的看到,shiftcls
的地址 = WJPerson类
地址 >> 3(向右移动了三位)
得到的。
网友评论