在所有的面向对象语言中,万物皆对象。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_createInstance
和 class_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
类型,我们主要设置 ISA
的 bits
、 has_cxx_dtor
、 shiftcls
三个属性。
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); }
这个方法的主要操作让我们很容易联想到 MRC
下 setter
方法的书写方式,保留新值,释放旧值。我们在进一步看下 objc_retain
和 objc_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
是否溢出,如果溢出则需要将一半的引用计数放到sidetable
的refcntStorage
中同时将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_t
从weaktable
中移除。 - 调用
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
中获取引用计数
对象的销毁
对象常用的销毁方法实际上就是 release
和 autorelease
两种。我们来看下这两种的具体实现:
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
方法,这个方法会判断sidetable
中refcnt
是否小于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
实际上最终是调用了 AutoreleasePoolPage
的 autorelease
方法, 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
且没有弱指针指向他,且没有关联对象,没有自定义销毁方法,sidetable
的retainCount = 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
调用sidetable_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的实现,尤其是对于引用计数器的操作。让我们对内存管理有了更清晰的认识。