Retain count được lưu trữ như nào

Chắc hẳn có nhiều bạn cũng như mình, thắc mắc không biết objective-C lưu trữ retain count như nào.

Hôm nay chúng ta sẽ mổ sẻ open source objc4 xem retain count được lưu như nào.

Đầu tiên chúng ta xuất phát từ implement của method -[NSObject retainCount] xem retain count được lưu ở đâu.

- (NSUInteger)retainCount {
    return ((id)self)->rootRetainCount();
}

Ta thấy nó gọi tiếp hàm C++ rootRetainCount. Ta tiếp tục xem nó làm gì tiếp.

Apple define 2 hàm rootRetainCount. Một hàm sử dụng chế độ non-pointer isa, một hàm sử dụng chế độ isa pointer tuỳ theo thiết bị mà sử dụng mode khác nhau.

Non-pointer isa mode

inline uintptr_t 
objc_object::rootRetainCount()
{
    if (isTaggedPointer()) return (uintptr_t)this;

    sidetable_lock();
    isa_t bits = LoadExclusive(&isa.bits);
    ClearExclusive(&isa.bits);
    if (bits.nonpointer) {
        uintptr_t rc = 1 + bits.extra_rc;
        if (bits.has_sidetable_rc) {
            rc += sidetable_getExtraRC_nolock();
        }
        sidetable_unlock();
        return rc;
    }

    sidetable_unlock();
    return sidetable_retainCount();
}

Khi ở chế độ non-pointer mode, Retain count sẽ được lấy ra từ trong extra_rc và side-table( nếu cờ has_sidetable_rc được bật).

size_t 
objc_object::sidetable_getExtraRC_nolock()
{
    assert(isa.nonpointer);
    SideTable& table = SideTables()[this];
    RefcountMap::iterator it = table.refcnts.find(this);
    if (it == table.refcnts.end()) return 0;
    else return it->second >> SIDE_TABLE_RC_SHIFT;
}

Hàm SideTables trả về một mảng băm chứa SideTable và SideTable là một struct chứa map refcnts lưu trữ retain count của object.

struct SideTable {
    spinlock_t slock;
    RefcountMap refcnts;
    weak_table_t weak_table;

    SideTable() {
        memset(&weak_table, 0, sizeof(weak_table));
    }

    ~SideTable() {
        _objc_fatal("Do not delete SideTable.");
    }

    void lock() { slock.lock(); }
    void unlock() { slock.unlock(); }
    void forceReset() { slock.forceReset(); }

    // Address-ordered lock discipline for a pair of side tables.

    template<HaveOld, HaveNew>
    static void lockTwo(SideTable *lock1, SideTable *lock2);
    template<HaveOld, HaveNew>
    static void unlockTwo(SideTable *lock1, SideTable *lock2);
};

Giá trị return sidetable_getExtraRC_nolock cộng với extra retain count + 1 ở trong isa là giá trị retain count cuối cùng ta cần lấy.

Nhìn vào đây các bạn cũng thấy weak reference cũng được lưu ở đây. Nó lưu lại nhằm mụch đích clear pointer khi vùng nhớ bị hủy.

Normal mode

inline uintptr_t 
objc_object::rootRetainCount()
{
    if (isTaggedPointer()) return (uintptr_t)this;
    return sidetable_retainCount();
}

Ở chế độ thường, thì retain count chỉ được lấy ở trong sidetable ra.

uintptr_t
objc_object::sidetable_retainCount()
{
    SideTable& table = SideTables()[this];

    size_t refcnt_result = 1;
    
    table.lock();
    RefcountMap::iterator it = table.refcnts.find(this);
    if (it != table.refcnts.end()) {
        // this is valid for SIDE_TABLE_RC_PINNED too
        refcnt_result += it->second >> SIDE_TABLE_RC_SHIFT;
    }
    table.unlock();
    return refcnt_result;
}

Hàm sidetable_retainCount tương tự như sidetable_getExtraRC_nolock đều lấy giá trị từ trong sidetable.

Tổng kết

Retain count sẽ được lưu vào isa và side table với mode non-pointer isa và chỉ lưu vào side table với normal mode.

Cảm ơn các bạn đã đọc bài.

Leave a Reply