iOS底层原理(八)内存管理(上)

iOS程序的内存布局

iOS程序的内存布局顺序如以下所示

我们可以通过打印内存地址来验证

int a = 10;
int b;

int main(int argc, char * argv[]) {
    @autoreleasepool {
        static int c = 20;
        
        static int d;
        
        int e;
        int f = 20;

        NSString *str = @"123";
        
        NSObject *obj = [[NSObject alloc] init];
        
        NSLog(@"\n&a=%p\n&b=%p\n&c=%p\n&d=%p\n&e=%p\n&f=%p\nstr=%p\nobj=%p\n",
              &a, &b, &c, &d, &e, &f, str, obj);
        
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

// 输出结果
&a=0x100c81e58
&b=0x100c81f24
&c=0x100c81e5c
&d=0x100c81f20
&e=0x7ffeeef80c2c
&f=0x7ffeeef80c28
str=0x100c81070
obj=0x6000024900a0

经过排序的内存地址大小如下

 字符串常量
 str=0x10dfa0068
 
 已初始化的全局变量、静态变量
 &a =0x10dfa0db8
 &c =0x10dfa0dbc
 
 未初始化的全局变量、静态变量
 &d =0x10dfa0e80
 &b =0x10dfa0e84
 
 堆
 obj=0x608000012210
 
 栈
 &f =0x7ffee1c60fe0
 &e =0x7ffee1c60fe4

注意: 字符串常量严格来说应该是存储在__TEXT段,只是我们习惯把他归于数据段

OC对象的一些特殊类型的内存管理

64bit开始,iOS引入了Tagged Pointer技术,用于优化NSNumber、NSDate、NSString等小对象的存储

Tagged Pointer的使用

  • 在没有使用Tagged Pointer之前, NSNumber等对象需要动态分配内存、维护引用计数等,NSNumber指针存储的是堆中NSNumber对象的地址值
  • 使用Tagged Pointer之后,NSNumber指针里面存储的数据变成了:Tag + Data,也就是将数据直接存储在了指针中
  • 当指针不够存储数据时,才会使用动态分配内存的方式来存储数据
  • objc_msgSend能识别Tagged Pointer,比如NSNumberintValue方法,直接从指针提取数据,节省了以前的调用开销

下面我们来举例说明,看示例代码

NSNumber *number1 = @4;
NSNumber *number2 = @5;
NSNumber *number3 = @(0xFFFFFFFFFFFFFFF);
   
NSLog(@"%p %p %p", number1, number2, number3);

// 输出地址分别为:0x5a05f784d24b4325 0x5a05f784d24b4225 0x100515970

我们知道,如果要是动态分配内存,由于OC对象都有isa指针,所以最少分配16个字节,换算成十六进制后末位都是0;由此可以推断使用了Tagged Pointer的内存地址末位不为0

然后再看上面示例代码的打印,前两个个值的末位都不为0,所以使用了Tagged Pointer来做优化;最后一个内存地址末位为0,证明由于需要存储的数据太大了,才会采用动态分配内存的方式

源码分析

我们可以从objc4objc-object.h找到isTaggedPointer的实现,是通过和一个掩码_OBJC_TAG_MASK进行按位与运算来判断是否使用了TaggedPointer

objc_object::isTaggedPointer() 
{
    return _objc_isTaggedPointer(this);
}

static inline bool 
_objc_isTaggedPointer(const void * _Nullable ptr)
{
    return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
}

_OBJC_TAG_MASK掩码的值

#if __arm64__
#   define OBJC_SPLIT_TAGGED_POINTERS 1
#else
#   define OBJC_SPLIT_TAGGED_POINTERS 0
#endif

#if (TARGET_OS_OSX || TARGET_OS_MACCATALYST) && __x86_64__
    // 64-bit Mac - tag bit is LSB
#   define OBJC_MSB_TAGGED_POINTERS 0
#else
    // Everything else - tag bit is MSB
#   define OBJC_MSB_TAGGED_POINTERS 1
#endif

#if OBJC_SPLIT_TAGGED_POINTERS
#   define _OBJC_TAG_MASK (1UL<<63) // 指针的最高有效位为1

#elif OBJC_MSB_TAGGED_POINTERS
#   define _OBJC_TAG_MASK (1UL<<63)

#else
#   define _OBJC_TAG_MASK 1UL // 指针的最低有效位为1

#endif

OC对象的内存管理

在iOS中,使用引用计数来管理OC对象的内存

引用计数的原则

  • 一个新创建的OC对象引用计数默认是1,当引用计数减为0,OC对象就会销毁,释放其占用的内存空间
  • 调用retain会让OC对象的引用计数+1,调用release会让OC对象的引用计数-1- 调用retain会让OC对象的引用计数+1,调用release会让OC对象的引用计数-1- 当调用alloc、new、copy、mutableCopy方法返回了一个对象,在不需要这个对象时,要调用release或者autorelease来释放它
  • 想拥有某个对象,就让它的引用计数+1;不想再拥有某个对象,就让它的引用计数-1

MRC环境下的内存管理

MRC环境下的内存管理使用,见下面代码

// Car
@interface Car : NSObject

@end

@implementation Car

@end

// Dog
@interface Dog : NSObject

- (void)run;
@end

@implementation Dog

- (void)run
{
    NSLog(@"%s", __func__);
}

- (void)dealloc
{
    [super dealloc];
    
    NSLog(@"%s", __func__);
}
@end

// Person
@interface Person : NSObject
{
    Dog *_dog;
    Car *_car;
    int _age;
}

- (void)setAge:(int)age;
- (int)age;

- (void)setDog:(Dog *)dog;
- (Dog *)dog;

- (void)setCar:(Car *)car;
- (Car *)car;

@end

@implementation Person

- (void)setAge:(int)age
{
    _age = age;
}

- (int)age
{
    return _age;
}

- (void)setDog:(Dog *)dog
{
    if (_dog != dog) {
        [_dog release];
        _dog = [dog retain];
    }
}

- (Dog *)dog
{
    return _dog;
}

- (void)setCar:(Car *)car
{
    if (_car != car) {
        [_car release];
        _car = [car retain];
    }
}

- (Car *)car
{
    return _car;
}

- (void)dealloc
{
//    [_dog release];
//    _dog = nil;
    self.dog = nil;
    self.car = nil;
    
    NSLog(@"%s", __func__);
    
    // 父类的dealloc放到最后
    [super dealloc];
}

@end


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        Dog *dog = [[Dog alloc] init]; // 1
        Person *person = [[Person alloc] init]; // 1
        
        [person setDog:dog]; // 2
        
        [person release]; // 0 // dog -1 = 1
        [dog release]; // 0
    }
    return 0;
}

加上@property后,编译器会自动生成setter和getter

@property (nonatomic, assign) int age;
@property (nonatomic, retain) Dog *dog;

类工厂方法创建的对象不需要进行retain操作,其内部已经对应做了处理

NSMutableArray *data = [NSMutableArray array];

copy

深拷贝和浅拷贝

拷贝的目的:产生一个副本对象,跟源对象互不影响

  • 修改了源对象,不会影响副本对象
  • 修改了副本对象,不会影响源对象

iOS提供了2个拷贝方法

  • copy:不可变拷贝,产生不可变副本
  • mutableCopy:可变拷贝,产生可变副本

深拷贝和浅拷贝

  • 深拷贝:内容拷贝,产生新的对象
  • 浅拷贝:指针拷贝,没有产生新的对象

看下面示例代码,内存地址分别是什么

NSString *str1 = [NSString stringWithFormat:@"test"];
NSString *str2 = [str1 copy]; // 返回的是NSString,浅拷贝
NSMutableString *str3 = [str1 mutableCopy]; // 返回的是NSMutableString,深拷贝

NSMutableString *str4 = [[NSMutableString alloc] initWithFormat:@"test"];
NSString *str5 = [str4 copy]; // 返回的是NSString,深拷贝
NSMutableString *str6 = [str4 mutableCopy]; // 返回的是NSMutableString,深拷贝

NSLog(@"%p %p %p", str1, str2, str3);
NSLog(@"%p %p %p", str4, str5, str6);

// copy、mutablecopy相当于进行了一次retain,要对应进行一次release
[str6 release];
[str5 release];
[str4 release];

[str3 release];
[str2 release];
[str1 release];

通过打印发现str1str2的内存地址是一样的,所以为浅拷贝,没有产生新的对象;而其他的都产生了新的对象,为深拷贝

常用的几个可变不可变的类型进行copy的操作如下图所示

copy修饰属性

1.我们看下面代码,运行结果是怎样,为什么

@interface Person : NSObject

@property (strong, nonatomic) NSString * text;
@end

@implementation Person

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *person = [[Person alloc] init];
        NSMutableString * str1 = [NSMutableString stringWithString:@"dddddddddddd"];
        person.text = str1;
        [str1 appendString:@"33"];
        
        NSLog(@"%@ %@", str1, person.text);
    }
    return 0;
}

// 输出:dddddddddddd33 dddddddddddd33

我们发现str1的值改变后,person.text也会被影响到,因为它们指向的是同一块内存空间

所以像NSString、NSArray、NSDictionary这几个类型作为属性,都是用copy来修饰的,这样意味着属性是不可改变的

@property (copy, nonatomic) NSArray *data;
@property (copy, nonatomic) NSString *text;
@property (copy, nonatomic) NSDictionary *dict;

2.我们再看下面代码,运行结果是怎样,为什么

@interface Person : NSObject

@property (copy, nonatomic) NSMutableArray *data;
@end

@implementation Person

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p = [[Person alloc] init];
        
        p.data = [NSMutableArray array];
        [p.data addObject:@"jack"];
        [p.data addObject:@"rose"];
    }
    return 0;
}

结果会报错。

因为用copy修饰NSMutableArray,生成的setter会通过深拷贝变成NSArray类型的对象,然后NSArray在进行添加元素自然会报错

所以像NSMutableString、NSMutableArray、NSMutableDictionary这几个类型作为属性,都是用strong来修饰的,这样意味着属性是不可改变的

@property (strong, nonatomic) NSMutableArray *data;
@property (strong, nonatomic) NSMutableString *text;
@property (strong, nonatomic) NSMutableDictionary *dict;

自定义copy

我们的自定义类型也可以通过遵守NSCopying协议实现拷贝功能

使用代码如下

@interface Person : NSObject <NSCopying>

@property (assign, nonatomic) int age;
@property (assign, nonatomic) double weight;
@end

@implementation Person

- (id)copyWithZone:(NSZone *)zone
{
    Person *person = [[Person allocWithZone:zone] init];
    person.age = self.age;
    person.weight = self.weight;
    return person;
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p1 = [[Person alloc] init];
        p1.age = 20;
        p1.weight = 50;

        Person *p2 = [p1 copy];
//        p2.age = 30;

        NSLog(@"%@", p1);
        NSLog(@"%@", p2);   
    }
    return 0;
}

引用计数的存储

我们之前学习的isa指针里有一个位域的值是用来存储引用计数器的

当引用计数器过大就会存储在一个叫SideTable的类中

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);
};

RefcountMap refcnts是一个散列表,用来存储着引用计数的

typedef objc::DenseMap<DisguisedPtr<objc_object>,size_t,RefcountMapValuePurgeable> RefcountMap;

通过源码分析

retain的源码分析

我们在objc-object.h中可以对应看到引用计数存储的相关代码

我们通过retain来分析,其内部会去调用rootRetain

inline id 
objc_object::retain()
{
    ASSERT(!isTaggedPointer());

    return rootRetain(false, RRVariant::FastOrMsgSend);
}

ALWAYS_INLINE id
objc_object::rootRetain(bool tryRetain, objc_object::RRVariant variant)
{
    if (slowpath(isTaggedPointer())) return (id)this;

    bool sideTableLocked = false;
    bool transcribeToSideTable = false;

    isa_t oldisa;
    isa_t newisa;

    oldisa = LoadExclusive(&isa.bits);

    if (variant == RRVariant::FastOrMsgSend) {
        // These checks are only meaningful for objc_retain()
        // They are here so that we avoid a re-load of the isa.
        if (slowpath(oldisa.getDecodedClass(false)->hasCustomRR())) {
            ClearExclusive(&isa.bits);
            if (oldisa.getDecodedClass(false)->canCallSwiftRR()) {
                return swiftRetain.load(memory_order_relaxed)((id)this);
            }
            return ((id(*)(objc_object *, SEL))objc_msgSend)(this, @selector(retain));
        }
    }

    if (slowpath(!oldisa.nonpointer)) {
        // a Class is a Class forever, so we can perform this check once
        // outside of the CAS loop
        if (oldisa.getDecodedClass(false)->isMetaClass()) {
            ClearExclusive(&isa.bits);
            return (id)this;
        }
    }

    do {
        transcribeToSideTable = false;
        newisa = oldisa;
        
        // 如果不是nonpointer,直接操作散列表+1
        if (slowpath(!newisa.nonpointer)) {
            ClearExclusive(&isa.bits);
            if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;
            else return sidetable_retain(sideTableLocked);
        }
        // don't check newisa.fast_rr; we already called any RR overrides
        if (slowpath(newisa.isDeallocating())) {
            ClearExclusive(&isa.bits);
            if (sideTableLocked) {
                ASSERT(variant == RRVariant::Full);
                sidetable_unlock();
            }
            if (slowpath(tryRetain)) {
                return nil;
            } else {
                return (id)this;
            }
        }
        uintptr_t carry;
        
        // 执行引用计数加1操作
        newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry);  // extra_rc++

        // 判断extra_rc是否满了,carry是标识符
        if (slowpath(carry)) {
            // newisa.extra_rc++ overflowed
            if (variant != RRVariant::Full) {
                ClearExclusive(&isa.bits);
                return rootRetain_overflow(tryRetain);
            }
            // Leave half of the retain counts inline and 
            // prepare to copy the other half to the side table.
            // 如果extra_rc满了,则拿出一半存储到side table散列表中
            if (!tryRetain && !sideTableLocked) sidetable_lock();
            sideTableLocked = true;
            transcribeToSideTable = true;
            newisa.extra_rc = RC_HALF;
            newisa.has_sidetable_rc = true;
        }
    } while (slowpath(!StoreExclusive(&isa.bits, &oldisa.bits, newisa.bits)));

    if (variant == RRVariant::Full) {
        if (slowpath(transcribeToSideTable)) {
            // Copy the other half of the retain counts to the side table.
            sidetable_addExtraRC_nolock(RC_HALF);
        }

        if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();
    } else {
        ASSERT(!transcribeToSideTable);
        ASSERT(!sideTableLocked);
    }

    return (id)this;
}

retain的流程可以用下图来概述

release的源码分析

然后我们再看release,会对应调用rootRelease

inline void
objc_object::release()
{
    ASSERT(!isTaggedPointer());

    rootRelease(true, RRVariant::FastOrMsgSend);
}

ALWAYS_INLINE bool
objc_object::rootRelease(bool performDealloc, objc_object::RRVariant variant)
{
    if (slowpath(isTaggedPointer())) return false;

    bool sideTableLocked = false;

    isa_t newisa, oldisa;

    oldisa = LoadExclusive(&isa.bits);

    if (variant == RRVariant::FastOrMsgSend) {
        // These checks are only meaningful for objc_release()
        // They are here so that we avoid a re-load of the isa.
        if (slowpath(oldisa.getDecodedClass(false)->hasCustomRR())) {
            ClearExclusive(&isa.bits);
            if (oldisa.getDecodedClass(false)->canCallSwiftRR()) {
                swiftRelease.load(memory_order_relaxed)((id)this);
                return true;
            }
            ((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(release));
            return true;
        }
    }

    // 判断nonpointer是不是共用体类型的指针
    if (slowpath(!oldisa.nonpointer)) {
        // a Class is a Class forever, so we can perform this check once
        // outside of the CAS loop
        if (oldisa.getDecodedClass(false)->isMetaClass()) {
            ClearExclusive(&isa.bits);
            return false;
        }
    }

retry:
    do {
        newisa = oldisa;
        
        // 判断是否为nonpointer
        if (slowpath(!newisa.nonpointer)) {
            ClearExclusive(&isa.bits);
            
            // 不是则直接操作散列表-1
            return sidetable_release(sideTableLocked, performDealloc);
        }
        if (slowpath(newisa.isDeallocating())) {
            ClearExclusive(&isa.bits);
            if (sideTableLocked) {
                ASSERT(variant == RRVariant::Full);
                sidetable_unlock();
            }
            return false;
        }

        // don't check newisa.fast_rr; we already called any RR overrides
        uintptr_t carry;
        // 进行引用计数-1操作(extra_rc--)
        newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry);  // extra_rc--
        if (slowpath(carry)) {
            // don't ClearExclusive()
            // 如果此时extra_rc的值为0了,则走到underflow
            goto underflow;
        }
    } while (slowpath(!StoreReleaseExclusive(&isa.bits, &oldisa.bits, newisa.bits)));

    // 此时extra_rc中值为0,散列表中也是空的,触发析构函数
    if (slowpath(newisa.isDeallocating()))
        goto deallocate;

    if (variant == RRVariant::Full) {
        if (slowpath(sideTableLocked)) sidetable_unlock();
    } else {
        ASSERT(!sideTableLocked);
    }
    return false;

 underflow:
    // newisa.extra_rc-- underflowed: borrow from side table or deallocate

    // abandon newisa to undo the decrement
    newisa = oldisa;

    // 判断散列表中是否存储了一半的引用计数
    if (slowpath(newisa.has_sidetable_rc)) {
        if (variant != RRVariant::Full) {
            ClearExclusive(&isa.bits);
            return rootRelease_underflow(performDealloc);
        }

        // Transfer retain count from side table to inline storage.

        if (!sideTableLocked) {
            ClearExclusive(&isa.bits);
            sidetable_lock();
            sideTableLocked = true;
            // Need to start over to avoid a race against 
            // the nonpointer -> raw pointer transition.
            oldisa = LoadExclusive(&isa.bits);
            goto retry;
        }

        // Try to remove some retain counts from the side table.
        // 从SideTable中移除存储的一半引用计数
        auto borrow = sidetable_subExtraRC_nolock(RC_HALF);

        bool emptySideTable = borrow.remaining == 0; // we'll clear the side table if no refcounts remain there

        if (borrow.borrowed > 0) {
            // Side table retain count decreased.
            // Try to add them to the inline count.
            bool didTransitionToDeallocating = false;
            
            // 进行-1操作,然后存储到extra_rc中
            newisa.extra_rc = borrow.borrowed - 1;  // redo the original decrement too
            newisa.has_sidetable_rc = !emptySideTable;

            bool stored = StoreReleaseExclusive(&isa.bits, &oldisa.bits, newisa.bits);

            if (!stored && oldisa.nonpointer) {
                // Inline update failed. 
                // Try it again right now. This prevents livelock on LL/SC 
                // architectures where the side table access itself may have 
                // dropped the reservation.
                uintptr_t overflow;
                newisa.bits =
                    addc(oldisa.bits, RC_ONE * (borrow.borrowed-1), 0, &overflow);
                newisa.has_sidetable_rc = !emptySideTable;
                if (!overflow) {
                    stored = StoreReleaseExclusive(&isa.bits, &oldisa.bits, newisa.bits);
                    if (stored) {
                        didTransitionToDeallocating = newisa.isDeallocating();
                    }
                }
            }

            if (!stored) {
                // Inline update failed.
                // Put the retains back in the side table.
                ClearExclusive(&isa.bits);
                sidetable_addExtraRC_nolock(borrow.borrowed);
                oldisa = LoadExclusive(&isa.bits);
                goto retry;
            }

            // Decrement successful after borrowing from side table.
            if (emptySideTable)
                sidetable_clearExtraRC_nolock();

            if (!didTransitionToDeallocating) {
                if (slowpath(sideTableLocked)) sidetable_unlock();
                return false;
            }
        }
        else {
            // Side table is empty after all. Fall-through to the dealloc path.
        }
    }

    // 进行析构,发送dealloc消息
deallocate:
    // Really deallocate.

    ASSERT(newisa.isDeallocating());
    ASSERT(isa.isDeallocating());

    if (slowpath(sideTableLocked)) sidetable_unlock();

    __c11_atomic_thread_fence(__ATOMIC_ACQUIRE);

    if (performDealloc) {
        ((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(dealloc));
    }
    return true;
}

release的流程可以用下图来概述

dealloc源码分析

retainrelease的实现中,都涉及到dealloc析构函数,我们来分析下dealloc的底层实现

通过调用轨迹dealloc -> _objc_rootDealloc -> rootDealloc,最终会调用到rootDealloc

inline void
objc_object::rootDealloc()
{
    if (isTaggedPointer()) return;  // fixme necessary?

    if (fastpath(isa.nonpointer                     && // 普通的isa
                 !isa.weakly_referenced             && // 弱指针引用
                 !isa.has_assoc                     && // 关联对象
#if ISA_HAS_CXX_DTOR_BIT
                 !isa.has_cxx_dtor                  && // c++析构函数
#else
                 !isa.getClass(false)->hasCxxDtor() &&
#endif
                 !isa.has_sidetable_rc)) // 引用计数散列表
    {
        assert(!sidetable_present());
        free(this); // 直接释放
    } 
    else {
        object_dispose((id)this);
    }
}

进一步调用到object_dispose

我们可以看到如果没有上述判断中需要处理的条件,对象会释放的更快

// object_dispose
id 
object_dispose(id obj)
{
    if (!obj) return nil;

    // 销毁实例而不释放内存
    objc_destructInstance(obj);
    // 释放内存
    free(obj);

    return nil;
}

// objc_destructInstance
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.
        // 调用c++析构函数,清除成员变量
        if (cxx) object_cxxDestruct(obj);
        // 删除关联对象
        if (assoc) _object_remove_assocations(obj, /*deallocating*/true);
        // 将指向当前对象的弱指针置为nil
        obj->clearDeallocating();
    }

    return obj;
}

// clearDeallocating
inline void 
objc_object::clearDeallocating()
{
    // 判断是否为nonpointer
    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());
}

// clearDeallocating_slow
NEVER_INLINE void
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();
}

dealloc的流程可以用下图概述

retainCount 源码分析

下面我们再来分析retainCount的实现

- (NSUInteger)retainCount {
    return _objc_rootRetainCount(self);
}

uintptr_t
_objc_rootRetainCount(id obj)
{
    ASSERT(obj);

    return obj->rootRetainCount();
}

内部会进一步调用rootRetainCount

inline uintptr_t 
objc_object::rootRetainCount()
{
    // 如果是TaggedPointer就返回
    if (isTaggedPointer()) return (uintptr_t)this;

    sidetable_lock();
    
    // 拿到isa
    isa_t bits = __c11_atomic_load((_Atomic uintptr_t *)&isa.bits, __ATOMIC_RELAXED);
    if (bits.nonpointer) { // 查看是否为非指针类型
        uintptr_t rc = bits.extra_rc; // 拿到isa指针里的extra_rc返回
        if (bits.has_sidetable_rc) { // 判断has_sidetable_rc的值是否为1,如果为1就要去SideTable里面取
            rc += sidetable_getExtraRC_nolock();
        }
        sidetable_unlock();
        return rc;
    }

    sidetable_unlock();
    return sidetable_retainCount();
}

size_t 
objc_object::sidetable_getExtraRC_nolock()
{
    // 通过一个key取出SideTable里的散列表refcnts
    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;
}
posted on 2021-04-09 05:04  FunkyRay  阅读(77)  评论(0编辑  收藏  举报