NSObject 底层原理分析(一)

本文详细探讨了Objective-C中NSObject类的底层原理,包括通过alloc、init和new创建对象的过程,以及对象的销毁机制。在初始化阶段,讲解了内存分配、isa指针的初始化以及对象实例的创建。在销毁阶段,阐述了dealloc方法如何释放资源,特别是对TaggedPointer对象的处理。此外,还提到了内存管理的关键细节,如优化的isa结构体和引用计数的处理。
摘要由CSDN通过智能技术生成


NSObject在Objective-C中大多数类层次结构的根类,通常我们在使用NSObject对象时,会使用[[NSObject alloc] init] 或者 [NSObject new]创建对象实例,通过这篇文章,我们一起研究关于NSObject的对象创建过程。

初始化

调用alloc方式

当我们调用[NSObject alloc]方法时候,会调用_objc_rootAlloc(self)这个方法,将NSObject自身作为参数传递下去申请一份内存。

callAlloc 接受三个参数:

  1. 类;
  2. 是否检查为空;
  3. 是否调用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库分配。
初始化流程:

  1. size = cls->instanceSize(extraBytes)初始化对象所需要的内存大小;
  2. 通过obj = (id)calloc(1, size)生成一份内存大小,malloc 不会设置内存为零,而 calloc 会设置分配的内存为零;
  3. 调用obj->initInstanceIsa(cls, hasCxxDtor),初始化isa与cls类进行关联;
  4. 返回对象;

调用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设备提出的一种存储小对象的技术,它具有以下特点:

  1. Tagged Pointer指针的值不再是地址了,而是真正的值。所以,实际上它不再是一个对象了,它只是一个披着对象皮的普通变量而已。
  2. 它的内存并不存储在堆中,也不需要 malloc 和 free,不走引用计数那一套逻辑,由系统来处理释放。
  3. 在内存读取上有着 3 倍的效率,创建时比以前快 106 倍。
  4. 可以通过设置环境变量OBJC_DISABLE_TAGGED_POINTERS来有开发者决定是否使用这项技术。
  1. Tagged Pointer判断,当返回值为真时,则说明当前指针指的指是对象而不是地址,应该由系统来处理释放,直接返回return。
  2. 判断当前isa是否经过优化,当nonpointer为1表示当前的isa是经过优化的;
  3. 判断当前isa是否被弱引用或曾经被弱引用过,!isa.weakly_referenced会更快的释放;
  4. 判断当前isa是否有关联对象,!isa.has_assoc会更快的释放;
  5. 判断当前isa是否有C++析构函数函数,!isa.has_cxx_dtor会更快的释放;
  6. 判断当前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)释放对象。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值