@[TOC](NSObject 底层原理分析)
NSObject在Objective-C中大多数类层次结构的根类,通常我们在使用NSObject对象时,会使用[[NSObject alloc] init] 或者 [NSObject new]创建对象实例,通过这篇文章,我们一起研究关于NSObject的对象创建过程。
初始化
调用alloc方式
当我们调用[NSObject alloc]方法时候,会调用_objc_rootAlloc(self)这个方法,将NSObject自身作为参数传递下去申请一份内存。
callAlloc 接受三个参数:
- 类;
- 是否检查为空;
- 是否调用allocWithZone;
define ALWAYS_INLINE inline attribute((always_inline))
采用内联函数
id _objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OBJC2__
if (slowpath(checkNil && !cls)) return nil;
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
return _objc_rootAllocWithZone(cls, nil);
}
#endif
// No shortcuts available.
if (allocWithZone) {
return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil);
}
return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));
}
当调用alloc方法时,我们可以通过底层调用清楚的看到Objc4采用Runtime机制发送了信息给了allocWithZone方法。此时,allocWithZone接收到Runtime发送的消息,被调用[cls allocWithZone:nil]。
+ (id)allocWithZone:(struct _NSZone *)zone {
return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone);
}
NEVER_INLINE
id
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)
{
// allocWithZone under __OBJC2__ ignores the zone parameter
return _class_createInstanceFromZone(cls, 0, nil,
OBJECT_CONSTRUCT_CALL_BADALLOC);
}
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);
}
if (slowpath(!obj)) {
if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
return _objc_callBadAllocHandler(cls);
}
return nil;
}
if (!zone && fast) {
obj->initInstanceIsa(cls, hasCxxDtor);
} 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()丢弃了zone参数,开发者即使自定义allocWithZone方法也无法指定想使用的zone,对象zone实际上由libmolloc库分配。
初始化流程:
- size = cls->instanceSize(extraBytes)初始化对象所需要的内存大小;
- 通过obj = (id)calloc(1, size)生成一份内存大小,malloc 不会设置内存为零,而 calloc 会设置分配的内存为零;
- 调用obj->initInstanceIsa(cls, hasCxxDtor),初始化isa与cls类进行关联;
- 返回对象;
调用init方式
调用[[NSObject alloc] init] 方法会返回allocWithZone:生成的obj对象。
- (id)init {
return _objc_rootInit(self);
}
调用new方式
[NSObject new]调用实际上自身调用了[[NSObject alloc] init],在默认init方式下可以直接调用new方法。
+ (id)new {
return [callAlloc(self, false/*checkNil*/) init];
}
销毁
调用dealloc
- (void)dealloc {
_objc_rootDealloc(self);
}
void
_objc_rootDealloc(id obj)
{
ASSERT(obj);
obj->rootDealloc();
}
inline void
objc_object::rootDealloc()
{
if (isTaggedPointer()) return; // fixme necessary?
if (fastpath(isa.nonpointer &&
!isa.weakly_referenced &&
!isa.has_assoc &&
!isa.has_cxx_dtor &&
!isa.has_sidetable_rc))
{
assert(!sidetable_present());
free(this);
}
else {
object_dispose((id)this);
}
}
_objc_isTaggedPointer判断是否标记当前指针为存储64bit小对象指针。
static inline bool
_objc_isTaggedPointer(const void * _Nullable ptr)
{
return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK; // 标记位标记该指针是否是tagged pointer
}
Tagged Pointer是苹果在64bit设备提出的一种存储小对象的技术,它具有以下特点:
- Tagged Pointer指针的值不再是地址了,而是真正的值。所以,实际上它不再是一个对象了,它只是一个披着对象皮的普通变量而已。
- 它的内存并不存储在堆中,也不需要 malloc 和 free,不走引用计数那一套逻辑,由系统来处理释放。
- 在内存读取上有着 3 倍的效率,创建时比以前快 106 倍。
- 可以通过设置环境变量OBJC_DISABLE_TAGGED_POINTERS来有开发者决定是否使用这项技术。
- Tagged Pointer判断,当返回值为真时,则说明当前指针指的指是对象而不是地址,应该由系统来处理释放,直接返回return。
- 判断当前isa是否经过优化,当nonpointer为1表示当前的isa是经过优化的;
- 判断当前isa是否被弱引用或曾经被弱引用过,!isa.weakly_referenced会更快的释放;
- 判断当前isa是否有关联对象,!isa.has_assoc会更快的释放;
- 判断当前isa是否有C++析构函数函数,!isa.has_cxx_dtor会更快的释放;
- 判断当前isa是否有扩展的引用计数。当一个对象的引用计数比较少时,其引用计数就记录在isa中,当引用计数大于某个值时就会采用sideTable来协助存储引用计数,!isa.has_sidetable_rc会更快的释放;
以上有一点不满足,则会调用以下方法进行释放操作。
id
object_dispose(id obj)
{
if (!obj) return nil;
objc_destructInstance(obj);
free(obj);
return nil;
}
void *objc_destructInstance(id obj)
{
if (obj) {
// Read all of the flags at once for performance.
bool cxx = obj->hasCxxDtor();
bool assoc = obj->hasAssociatedObjects();
// This order is important.
if (cxx) object_cxxDestruct(obj);
if (assoc) _object_remove_assocations(obj);
obj->clearDeallocating();
}
return obj;
}
判断obj当前对象持有C++的析构函数和关联对象,并释放cxx与assoc。
当前判断obj是否是经过isa优化的,当slowpath(!isa.nonpointer)为真时,说明isa经过优化,通过SlideTable优化。从SlideTable表中移除。
inline void
objc_object::clearDeallocating()
{
if (slowpath(!isa.nonpointer)) {
// Slow path for raw pointer isa.
sidetable_clearDeallocating();
}
else if (slowpath(isa.weakly_referenced || isa.has_sidetable_rc)) {
// Slow path for non-pointer isa with weak refs and/or side table data.
clearDeallocating_slow();
}
assert(!sidetable_present());
}
void
objc_object::sidetable_clearDeallocating()
{
SideTable& table = SideTables()[this];
// clear any weak table items
// clear extra retain count and deallocating bit
// (fixme warn or abort if extra retain count == 0 ?)
table.lock();
RefcountMap::iterator it = table.refcnts.find(this);
if (it != table.refcnts.end()) {
if (it->second & SIDE_TABLE_WEAKLY_REFERENCED) {
weak_clear_no_lock(&table.weak_table, (id)this);
}
table.refcnts.erase(it);
}
table.unlock();
}
objc_object::clearDeallocating_slow()
{
ASSERT(isa.nonpointer && (isa.weakly_referenced || isa.has_sidetable_rc));
SideTable& table = SideTables()[this];
table.lock();
if (isa.weakly_referenced) {
weak_clear_no_lock(&table.weak_table, (id)this);
}
if (isa.has_sidetable_rc) {
table.refcnts.erase(this);
}
table.unlock();
}
完成以上操作后,进行free(obj)释放对象。