Runtime之对象的一生

在所有的面向对象语言中,万物皆对象。Objective-C也不例外,这篇文章我们从Runtime的源码去分析一个对象从创建到销毁的整个过程中都做了哪些操作,同时也加深我们对内存管理的理解。

对象的创建

我们平时开发中创建一个对象的方式一般有三种

  • alloc init
NSString *str = [[NSString alloc] init];
  • new
NSString *str = [NSString new];
  • 类方法构建
NSMutableString *str = [NSMutableString string];

那么在这个过程中系统都做了哪些操作呢?这三种不同的创建方式又会有什么区别呢?下面我们对上述的几个关键词一一介绍然后在分析下这几种创建对象方式的区别。

alloc

我们先来看下alloc方法在Runtime中的实现

+ (id)alloc {
return _objc_rootAlloc(self);
}

我们在进一步看下_objc_rootAlloc方法的实现:

id
_objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}

我们继续跟进方法实现:

// ALWAYS_INLINE 强制内联函数 所有加了__attribute__((always_inline))的函数再
// 把被调用时不会被编译成函数调用而是直接扩展到调用函数体内
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
// slowpath 表示if条件为0的可能性较大 利于编译器优化指令跳转
// 如果需要检查nil 且cls 为nil 那么直接返回nil
// 因此 如果cls不存在使用alloc方法创建时可能返回nil
if (slowpath(checkNil && !cls)) return nil;
#if __OBJC2__
// fastpath 表示if条件中为1的可能性较大
// 如果类没有自定义的allocwithzone方法
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
// cls 是否可以快速创建
if (fastpath(cls->canAllocFast())) {
// No ctors, raw isa, etc. Go straight to the metal.
// cls是否有c++销毁方法 dtor = destructor
bool dtor = cls->hasCxxDtor();
// calloc 在内存中动态地分配 1 个长度为  cls->bits.fastInstanceSize() 的连续空间
id obj = (id)calloc(1, cls->bits.fastInstanceSize());
// 如果obj为空 类初始化失败 报错 attempt to allocate object of class '%s' failed
if (slowpath(!obj)) return callBadAllocHandler(cls);
// 调用初始化对象isa方法
obj->initInstanceIsa(cls, dtor);
return obj;
}
else {
// Has ctor or raw isa or something. Use the slower path.
// 调用class_createInstance创建对象
id obj = class_createInstance(cls, 0);
// 如果对象为空 那么直接报错
if (slowpath(!obj)) return callBadAllocHandler(cls);
return obj;
}
}
#endif
// No shortcuts available.
// alloc 方法会进一步调用allocwithzone方法
if (allocWithZone) return [cls allocWithZone:nil];
// 正常情况不会走到这里 想到与一个递归?
return [cls alloc];
}

我们将这个方法的流程通过下图来更好的了解下:

注意 :图中深色背景区域为 OBJC2 下会走的的位置,基本上目前都会做到这里


为了更好的了解这个方法的实现我们进一步看下这个方法中调用的几个方法:

写完这个方法后, 发现在最新的Runtime(781)源码中这个方法变成了:

static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OBJC2__
if (slowpath(checkNil && !cls)) return nil;
// 是否有自定义的allocWithZone方法
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
// 直接调用_objc_rootAllocWithZone
return _objc_rootAllocWithZone(cls, nil);
}
#endif
// No shortcuts available.
// 是否需要调用allocWithZone _objc_rootAlloc方法调用时这个参数为yes
if (allocWithZone) {
return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil);
}
// 调用alloc方法
return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));
}

实际上在 OBJC2 中这个方法的改变并不大,下面我们看下上面这两种实现用到的主要方法的实现,

objc_rootAllocWithZone

//allocWithZone的根方法
id
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone)
{
id obj;
// 如果zone未空 那么直接调用class_createInstance方法 与之前750的实现一致
if (fastpath(!zone)) {
obj = class_createInstance(cls, 0);
} else {
// 如果返回的zone有值 那么调用class_createInstanceFromZone传入
obj = class_createInstanceFromZone(cls, 0, zone);
}
// 如果创建的obj为空
if (slowpath(!obj)) obj = _objc_callBadAllocHandler(cls);
return obj;
}

这个方法实际上只是做了一层简单的包装,我们重点关注 class_createInstanceclass_createInstanceFromZone 方法。

class_createInstance

id
class_createInstance(Class cls, size_t extraBytes)
{
if (!cls) return nil;
return _class_createInstanceFromZone(cls, extraBytes, nil);
}

我们继续看 _class_createInstanceFromZone 方法(即 _objc_rootAllocWithZone 方法中 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);
}

继续深入 _objc_rootAllocWithZone 方法:

// 创建对象
// cls 创建对象的类型
// extraBytes 额外字节 正常alloc方法这个值为0
// zone construct_flags outAllocatedSize
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();
// canAllocNonpointer:class's instances requires raw isa
// #define FAST_CACHE_REQUIRES_RAW_ISA   (1<<13)
// 是否是 isa_t 类型的 isa
bool fast = cls->canAllocNonpointer();
size_t size;
// 增加额外字节
size = cls->instanceSize(extraBytes);
if (outAllocatedSize) *outAllocatedSize = size;
id obj;
// 新创建一个obj 并指向一块新创建的内存空间
if (zone) {
obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
} else {
obj = (id)calloc(1, size);
}
// 如果对象创建失败 obj = nil
if (slowpath(!obj)) {
if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
return _objc_callBadAllocHandler(cls);
}
return nil;
}
// isa如果是isa_t类型
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.
// isa是cls类型
obj->initIsa(cls);
}
// 如果没有自定义构建方法
if (fastpath(!hasCxxCtor)) {
return obj;
}
// 自定义构建方法调用
construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
return object_cxxConstructFromClass(obj, cls, construct_flags);
}

我们在看下这个方法中用到的几个方法:

hasCxxDtor

bool hasCxxDtor() {
// addSubclass() propagates this flag from the superclass.
assert(isRealized());
return bits.hasCxxDtor();
}
bool hasCxxDtor() {
return data()->flags & RW_HAS_CXX_DTOR;
}
// class or superclass has .cxx_destruct implementation
#define RW_HAS_CXX_DTOR       (1<<17)

从上述代码中,我们可以看到 hasCxxDtor 表示 cls 或者 supercls 是否有销毁 (cxx_destruct) 方法。

canAllocNonpointer

BOOL canAllocNonpointer() {
ASSERT(!isFuture());
return !instancesRequireRawIsa();
}
BOOL instancesRequireRawIsa() {
return cache.getBit(FAST_CACHE_REQUIRES_RAW_ISA);
}
// class's instances requires raw isa
#define FAST_CACHE_REQUIRES_RAW_ISA   (1<<13)

initInstanceIsa

inline void
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
assert(!cls->instancesRequireRawIsa());
assert(hasCxxDtor == cls->hasCxxDtor());
// 相当于也是包装了一层
initIsa(cls, true, hasCxxDtor);
}

initInstanceIsa 主要是调用 initIsa 方法,我们在进一步看下这个方法的实现:

inline void
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
ASSERT(!isTaggedPointer());
// 如果是非isa_t类型 直接返回 这个cls即可 因为isa中只有一个指针指向isa
if (!nonpointer) {
isa = isa_t((uintptr_t)cls);
} else {
ASSERT(!DisableNonpointerIsa);
ASSERT(!cls->instancesRequireRawIsa());
// 创建 isa_t 类型临时变量
isa_t newisa(0);
// 配置 magic 表示当前对象已经创建
// 配置 nonpointer 表示当前对象的 isa 为 isa_t 类型的
newisa.bits = ISA_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
// 配置 has_cxx_dtor 表示当前对象是否有 C++ 的析构器
newisa.has_cxx_dtor = hasCxxDtor;
// 配置 shiftcls 指向类对象,右移了 3 位是因为类的指针是按照字节(8bits)对齐的,
// 其指针后三位都是没有意义的 0,因此可以右移 3 位进行消除,以减小无意义的内存占用。
newisa.shiftcls = (uintptr_t)cls >> 3;
// 将临时变量赋值给结构体成员
isa = newisa;
}
}

从上面我们看出如果是 isa_t 类型,我们主要设置 ISAbitshas_cxx_dtorshiftcls 三个属性。

hasCustomAWZ

bool hasCustomAWZ() {
return ! bits.hasDefaultAWZ();
}
bool hasDefaultAWZ() {
return data()->flags & RW_HAS_DEFAULT_AWZ;
}
// class or superclass has default alloc/allocWithZone: implementation
// Note this is is stored in the metaclass.
#define RW_HAS_DEFAULT_AWZ    (1<<16)

RW_HAS_DEFAULT_AWZ 这个标志位表明是 cls 或者 superclass 是否有默认的 alloc/allocWithZone 方法实现, hasCustomAWZ 的意思是是否有自定义的 AWZ(allocWithZone) 方法,在我们前面的NSObject结构这篇文章中我们介绍过下面这张图

在图中我们看到 NSObject 结构中有一个 uint32_t 类型的 flags ,而 hasDefaultAWZ 方法就是从我们的 NSObject 结构中通过按位与的方法获取到这个值。

object_cxxConstructFromClass

// 递归调用对象的C++构造方法,从基类到子类的构造方法
id
object_cxxConstructFromClass(id obj, Class cls, int flags)
{
ASSERT(cls->hasCxxCtor());  // required for performance, not correctness
id (*ctor)(id);
Class supercls;
supercls = cls->superclass;
// Call superclasses' ctors first, if any.
// 如果父类有C++构造方法
if (supercls  &&  supercls->hasCxxCtor()) {
// 调用父类构造方法
bool ok = object_cxxConstructFromClass(obj, supercls, flags);
// 父类构造方法失败 返回nil
if (slowpath(!ok)) return nil;  // some superclass's ctor failed - give up
}
// Find this class's ctor, if any.
// 父类构造方法调用完成后调用当前类的SEL_cxx_construct方法
ctor = (id(*)(id))lookupMethodInClassAndLoadCache(cls, SEL_cxx_construct);
// 如果当前类的构造为转发方法 那么直接返回 表示没有对应实现
if (ctor == (id(*)(id))_objc_msgForward_impcache) return obj;  // no ctor - ok
// Call this class's ctor.
if (PrintCxxCtors) {
_objc_inform("CXX: calling C++ constructors for class %s",
cls->nameForLogging());
}
// 调用构造方法 传入参数obj
if (fastpath((*ctor)(obj))) return obj;  // ctor called and succeeded - ok
supercls = cls->superclass; // this reload avoids a spill on the stack
// This class's ctor was called and failed.
// Call superclasses's dtors to clean up.
if (supercls) object_cxxDestructFromClass(obj, supercls);
if (flags & OBJECT_CONSTRUCT_FREE_ONFAILURE) free(obj);
if (flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
return _objc_callBadAllocHandler(cls);
}
return nil;
}

allocWithZone

// Replaced by ObjectAlloc
+ (id)allocWithZone:(struct _NSZone *)zone {
return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone);
}

这里实际上又回到了上面的 _objc_rootAllocWithZone 方法,与 OBJC2 的区别只是是否做了 cls 为nil的判断以及是否有自定义的 allocWithZone 的方法。

if (slowpath(checkNil && !cls)) return nil;
// 是否有自定义的allocWithZone方法
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
// 直接调用_objc_rootAllocWithZone
return _objc_rootAllocWithZone(cls, nil);
}

alloc 方法调用完成之后,我们实际上已经开辟了对应的内存空间初始化好了 ISA 的部分信息,下面我们来看下 init 方法。

init

- (id)init {
return _objc_rootInit(self);
}
id
_objc_rootInit(id obj)
{
// In practice, it will be hard to rely on this function.
// Many classes do not properly chain -init calls.
return obj;
}

从这里我们看到 init 方法实际上并没有实现什么只是将我们在 alloc 方法创建好的对象返回了。

new

+ (id)new {
return [callAlloc(self, false/*checkNil*/) init];
}

我们看到这个方法实际上就是调用了 callAlloc 方法然后 callAlloc 返回的对象在调用 init 方法。

至此对象创建的几种方式我们就分析完成了。

对象创建流程如下(主要是alloc方法):

对象赋值

看完了对象的初始化,我们在平时常用的就是给对象赋值和拷贝,比如

Person *person = [[Person alloc] init];
Person *personCpy = [person copy];

下面我们主要从上面这两个方面来看下在这个过程中 Runtime 都做了什么:

赋值

我们都知道,对于一个对象的引用我们有弱引用和强引用两种,分别用 __weak__strong 来修饰,如果我们不做显示说明那么默认是 __strong

我们在Runtime中去查找修饰变量的类型时发现了下面这个枚举:

/*
"Unknown" includes non-object ivars and non-ARC non-__weak ivars
"Strong" includes ARC __strong ivars
"Weak" includes ARC and new MRC __weak ivars
"Unretained" includes ARC __unsafe_unretained and old GC+MRC __weak ivars
*/
typedef enum {
objc_ivar_memoryUnknown,     // unknown / unknown
objc_ivar_memoryStrong,      // direct access / objc_storeStrong
objc_ivar_memoryWeak,        // objc_loadWeak[Retained] / objc_storeWeak
objc_ivar_memoryUnretained   // direct access / direct access
} objc_ivar_memory_management_t;

下面我们来详细的了解下这其中的区别:

__strong

上面看到的

Person *stu = [[Person alloc] init];

实际上可以翻译成:

__strong Person *stu = [[Person alloc] init];

那么 __strong 都做了什么呢?我们直接去看下 objc_storeStrong 方法都做了什么:

void
objc_storeStrong(id *location, id obj)
{
id prev = *location;
// 先判断之前的值和要新赋值的对象地址是否相同
if (obj == prev) {
return;
}
// 执行retain操作 保留新值
objc_retain(obj);
*location = obj;
// 释放旧值
objc_release(prev);
}

这个方法的主要操作让我们很容易联想到 MRCsetter 方法的书写方式,保留新值,释放旧值。我们在进一步看下 objc_retainobjc_release 的实现。

objc_retain

__attribute__((aligned(16), flatten, noinline))
id
objc_retain(id obj)
{
// 如果为nil 直接返回nil
if (!obj) return obj;
// 如果是taggedPointer 直接返回
if (obj->isTaggedPointer()) return obj;
// 调用retain方法
return obj->retain();
}

这个方法里 判断了如果要被 retain 的对象为 nil 或者是 TaggedPointer 类型那么直接返回否则调用 retain 方法。

// 等价于直接使用对象调用retain方法
inline id
objc_object::retain()
{
// 如果是TaggedPointer类型 不涉及引用计数
ASSERT(!isTaggedPointer());
// fastpath 表示if中的条件是一个大概率事件
// 如果当前对象没有自定义(override)retain 方法
if (fastpath(!ISA()->hasCustomRR())) {
return rootRetain();
}
// 如果有自定义的retain方法
// 通过发消息的方式调用自定义的 retain 方法
return ((id(*)(objc_object *, SEL))objc_msgSend)(this, @selector(retain));
}

这个方法实际上最后调用了 rootRetain ,对于这个方法我们在

retain和release实现探究 这篇文章中详细的介绍过。这里我们就不做进一步的介绍了。我们只需要知道在这个方法中我们 newisa.nonpointer 判断需要增加哪里的引用计数:

  • 如果 newisa.nonpointer = 0 则没有进行 runtime 优化,在 sidetable 中找到这个对象对应的节点更新将 SideTable 该对象对应的节点的 refcntStorage 值 + 1
  • 如果 newisa.nonpointer = 1 则进行了 runtime 优化,那么直接在 newisa.extra_rc 上做引用计数 +1 同时需要判断 newisa.extra_rc 是否溢出,如果溢出则需要将一半的引用计数放到 sidetablerefcntStorage 中同时将 newisa.has_sidetable_rc 置为true。

从上面可以看出 objc_retain 的操作实际是: 将新对象的引用计数+1,将旧对象的引用计数-1

__weak

当我们用 __weak 去修饰一个变量时根据上面的介绍我们实际上是调用 objc_storeWeak 方法,我们直接看下这个方法的实现。

// location weak指针自身的地址
// newObj weak指针指向的新对象的地址
id
objc_storeWeak(id *location, id newObj)
{
return storeWeak<DoHaveOld, DoHaveNew, DoCrashIfDeallocating>
(location, (objc_object *)newObj);

这个方法内部实际调用了 storeWeak 方法,这个方法与 rootRetain 一起,我们在 retain和release实现探究 这里都有详细的讲解这里不再赘述。

我们大概描述下 storeWeak 操作做了哪些事情:

  • 根据对象地址从 SideTable 中取出对应的节点,如果之前没有 则创建一个新的
  • 调用 weak_unregister_no_lock ,将 weak pointer 的地址从旧对象的 weak_entry_t 中移除,同时判断是否 weak_entry_t 为空,则将 weak_entry_tweaktable 中移除。
  • 调用 weak_register_no_lock 方法,将 weak pointer 的地址(location)记录到对象对应的 weak_entry_t 中,并将对象的isa指针中的 weakly_referenced 标志位置为1,表明这个对象被弱引用了

retaincount

我们上面创建或者给一个strong类型的指针赋值,实际上都是对引用计数的+1操作,那么我们看下这个引用计数是如何保存的呢?

- (NSUInteger)retainCount {
return _objc_rootRetainCount(self);
}
uintptr_t
_objc_rootRetainCount(id obj)
{
ASSERT(obj);
return obj->rootRetainCount();
}
// 获取引用高技术
inline uintptr_t
objc_object::rootRetainCount()
{
//isTaggedPointer 不需要引用计数
if (isTaggedPointer()) return (uintptr_t)this;
sidetable_lock();
isa_t bits = LoadExclusive(&isa.bits);
ClearExclusive(&isa.bits);
// 如果是nonpointer
if (bits.nonpointer) {
// 先从extra_rc取出部分引用计数
uintptr_t rc = 1 + bits.extra_rc;
// sidetable中是否有额外的引用计数
if (bits.has_sidetable_rc) {
// 从sidetable中获取引用计数
rc += sidetable_getExtraRC_nolock();
}
sidetable_unlock();
return rc;
}
sidetable_unlock();
// 如果不是nonpointer 直接从sidetable中获取引用计数
return sidetable_retainCount();
}

如果对象是 TaggedPointer 直接返回这个对象( TaggedPointer 不需要引用计数),否则

  • bits.nonpointer = 1 则先从 bits.extra_rc 获取部分引用计数 然后判断 sidetable 中是否有额外的引用计数 如果有 二者相加
  • bits.nonpointer = 0 则直接从 sidetable 中获取引用计数

对象的销毁

对象常用的销毁方法实际上就是 releaseautorelease 两种。我们来看下这两种的具体实现:

release

在查看源码前,我们知道 release 的主要功能是: 引用计数-1 ,我们来看下具体实现

- (oneway void)release {
_objc_rootRelease(self);
}

我们看下_objc_rootRelease的实现:

NEVER_INLINE void
_objc_rootRelease(id obj)
{
ASSERT(obj);
obj->rootRelease();
}

rootRelease 方法我们在 retain和release实现探究 中也有详细介绍。

我们简单的说下 rootRelease 这个方法:

  • 如果 newisa.nonpointer = 0 则调用 sidetable_release 方法,这个方法会判断 sidetablerefcnt 是否小于 SIDE_TABLE_DEALLOCATING 如果小于则表示这个对象要被释放,则调用 dealloc 方法。如果不小于则直接执行 refcnt -= SIDE_TABLE_RC_ONE ;即 refcnt-1 操作。
  • 如果 newisa.nonpointer = 1 则 对 newisa.extra_rc 进行-1操作,然后判断 newisa.extra_rc 是否有向下溢出,并执行对应操作。

autorelease

- (id)autorelease {
return _objc_rootAutorelease(self);
}
__attribute__((noinline,used))
id
objc_object::rootAutorelease2()
{
ASSERT(!isTaggedPointer());
return AutoreleasePoolPage::autorelease((id)this);
}
static inline id autorelease(id obj)
{
ASSERT(obj);
ASSERT(!obj->isTaggedPointer());
id *dest __unused = autoreleaseFast(obj);
ASSERT(!dest  ||  dest == EMPTY_POOL_PLACEHOLDER  ||  *dest == obj);
return obj;
}
static inline id *autoreleaseFast(id obj)
{
AutoreleasePoolPage *page = hotPage();
if (page && !page->full()) {
return page->add(obj);
} else if (page) {
return autoreleaseFullPage(obj, page);
} else {
return autoreleaseNoPage(obj);
}
}

我们看到 autorelease 实际上最终是调用了 AutoreleasePoolPageautorelease 方法, AutoreleasePoolPage 我们在 iOS内存管理之AutoreleasePool 中有详细介绍,这里我们也不再赘述。

我们简单的回忆下 AutoreleasePoolPage 几个重要方法:

  • push

    1、首先取出当前的 hotPage ,所谓 hotPage ,就是在 autoreleasePage 链表中正在使用的 autoreleasePage 节点。

    2、如果有 hotPage ,且 hotPage 还没满,这将obj加入到page中。

    3、如果有 hotPage ,但是已经满了,则进入 page full 逻辑(autoreleaseFullPage)

    4、如果没有 hotPage ,进入no page逻辑 autoreleaseNoPage

  • pop(void pop(void *token))

    autoreleasepool 需要被释放时,会调用 Pop 方法。而Pop方法需要接受一个 void *token 参数,来告诉池子,需要一直释放到token对应的那个page

通过上面的介绍,我们知道 AutoreleasePoolPage 实际上就是一个存放待释放对象的缓存池。push为追加对象,pop为释放对象。

dealloc

在上面我们介绍 release 时,我们发现在对对象的引用计数进行-1操作时,如果判断对象引用计数为0,会主动调用 dealloc ,那么我们看下这个方法的实现

- (void)dealloc {
_objc_rootDealloc(self);
}
_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);
}
}

我们看到这个方法主要逻辑为:

  • 如果对象 isa.nonpointer=1 且没有弱指针指向他,且没有关联对象,没有自定义销毁方法, sidetableretainCount = 0 则直接释放
  • 如果对象 isa.nonpointer=0 则需要调用 object_dispose 方法

我们在看下 object_dispose 方法的实现:

static id
_object_dispose(id anObject)
{
if (anObject==nil) return nil;
// 调用objc_destructInstance
objc_destructInstance(anObject);
// isa指针为_objc_getFreedObjectClass类型
anObject->initIsa(_objc_getFreedObjectClass ());
// 释放对象
free(anObject);
return nil;
}

我们进一步看下 _object_dispose 中的这几个方法

objc_destructInstance

// 对象的析构方法
void *objc_destructInstance(id obj)
{
if (obj) {
// 获取到isa
Class isa = obj->getIsa();
// 是否有c++析构方法 有则执行
if (isa->hasCxxDtor()) {
object_cxxDestruct(obj);
}
// 是否有关联对象 有则移除
if (isa->instancesHaveAssociatedObjects()) {
_object_remove_assocations(obj);
}
// 调用objc_clear_deallocating方法
objc_clear_deallocating(obj);
}
return obj;
}

initIsa

这个方法我们在上面介绍过了 这里主要看下参数 _objc_getFreedObjectClass () ,我们看下系统给的解释

/***********************************************************************
* _class_getFreedObjectClass.  Return a pointer to the dummy freed
* object class.  Freed objects get their isa pointers replaced with
* a pointer to the freedObjectClass, so that we can catch usages of
* the freed object.
**********************************************************************/
static Class _class_getFreedObjectClass(void)
{
return (Class)freedObjectClass;
}

我们可以简单的理解为,对于一个已释放的对象他的isa指针指向了 freedObjectClass 类型,系统以此作为已释放对象的标记。

我们在源码中搜索的时候还发现:

if (anObject->ISA() == _objc_getFreedObjectClass ())
__objc_error(anObject, "reallocating freed object");

重新分配已释放对象的空间,这也作证了我们上面描述的。

objc_clear_deallocating

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

这个方法也是根据 isa.nonpointer 判断:

  • 如果 isa.nonpointer = 0 调用s idetable_clearDeallocating 执行 sidetable 相关的释放
  • 如果 isa.nonpointer = 1 调用 clearDeallocating_slow 进行释放

sidetable_clearDeallocating

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();
}
  • 根据 this 取出对象对应的 sidetable 如果有弱引用指针指向这个对象 则这里进行置nil 调用的是 weak_clear_no_lock 方法
  • 调用 erase 方法将 table.refcnts 置为空

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

实际上这个方法和 sidetable_clearDeallocating 方法的实现高度相似,只是标志位一个是从 it->second 通过按位与方法判断一个是直接从isa中获取。

总结

这篇文章我们从对象的创建以及赋值销毁等方面分析了Runtime的实现,尤其是对于引用计数器的操作。让我们对内存管理有了更清晰的认识。

参考文章

leewong
我还没有学会写个人说明!
下一篇

数学好=编程能力强?MIT新发现:二者激活大脑区域并不同

你也可能喜欢

评论已经被关闭。

插入图片