iOS 这部分代码位于 objc-runtime/runtime/NSObject.mm:

1 对象的创建 alloc/init:

调用栈

--- alloc 
--- _objc_rootAlloc
--- callAlloc
--- allocWithZone
--- class_createInstance
--- _class_createInstanceFromZone
--- calloc
--- initIsa

--- init
--- _objc_rootInit

我们在创建对象的时候会调用 alloc 方法为要创建的对象分配内存空间:

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

在alloc中会调用_objc_rootAlloc

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

在_objc_rootAlloc中会调用callAlloc,它有两个参数第一个参数表示是否检查cls参数是否为空,第二个参数表示是否在zone中分配这个内存。

static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil/*false*/, bool allocWithZone=false/*true*/)
{
if (slowpath(checkNil && !cls)) return nil;

#if __OBJC2__
//hasCustomAWZ( )方法是用来判断当前class是否有自定义的allocWithZone。
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
// 没有自定义的alloc/allocWithZone实现的时候
// No alloc/allocWithZone implementation. Go straight to the allocator.
// fixme store hasCustomAWZ in the non-meta class and
// add it to canAllocFast's summary
if (fastpath(cls->canAllocFast())) {
// No ctors, raw isa, etc. Go straight to the metal.
bool dtor = cls->hasCxxDtor();
id obj = (id)calloc(1, cls->bits.fastInstanceSize());
if (slowpath(!obj)) return callBadAllocHandler(cls);
obj->initInstanceIsa(cls, dtor);
return obj;
}
else {
// Has ctor or raw isa or something. Use the slower path.
id obj = class_createInstance(cls, 0);
if (slowpath(!obj)) return callBadAllocHandler(cls);
return obj;
}
}
#endif

// No shortcuts available.
if (allocWithZone) return [cls allocWithZone:nil];
return [cls alloc];
}

首先看下hasCustomAWZ

bool hasCustomAWZ() {
return ! bits.hasDefaultAWZ();
}

bool hasDefaultAWZ( ) {
return data()->flags & RW_HAS_DEFAULT_AWZ;
}

#define RW_HAS_DEFAULT_AWZ (1<<16)

它实际上是查看metaclass 中的对应标识位flags的RW_HAS_DEFAULT_AWZ来查看当前的class或者是superclass是否有默认的alloc/allocWithZone:如果这个标志位被标记了那么hasCustomAWZ就为NO.

cls->canAllocFast() 这个一般情况下会返回false. 代码中有注解No ctors, raw isa, etc的情况下cls->canAllocFast() 返回YES.具体还不是很清楚这是什么意思。待弄明白后补上,这里记得返回false就好。

我们以最常见的:没有自定义alloc/allocWithZone:的情况作为简化后的分析对象:
这时候代码可以简化为:

static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil/*false*/, bool allocWithZone=false/*true*/)
{
// No shortcuts available.
if (allocWithZone) return [cls allocWithZone:nil];
return [cls alloc];
}

我们继续往下看:

+ (id)allocWithZone:(struct _NSZone *)zone {
return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone);
}
id
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone)
{
id obj;

#if __OBJC2__
// allocWithZone under __OBJC2__ ignores the zone parameter
(void)zone;
obj = class_createInstance(cls, 0);
#else
if (!zone) {
obj = class_createInstance(cls, 0);
}
else {
obj = class_createInstanceFromZone(cls, 0, zone);
}
#endif

if (slowpath(!obj)) obj = callBadAllocHandler(cls);
return obj;
}

我们记住这里zone传进去的是nil,所以_objc_rootAllocWithZone代码简化后如下:

id
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone)
{
id obj;

(void)zone;
obj = class_createInstance(cls, 0);

if (slowpath(!obj)) obj = callBadAllocHandler(cls);
return obj;
}


id
class_createInstance(Class cls, size_t extraBytes)
{
return _class_createInstanceFromZone(cls, extraBytes, nil);
}
static __attribute__((always_inline)) 
id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
bool cxxConstruct = true,
size_t *outAllocatedSize = nil)
{
// Read class's info bits all at once for performance
// 当前class或者superclass 是否有构造方法的实现
bool hasCxxCtor = cls->hasCxxCtor();
// 判断当前class或者superclass 是否有析构方法的实现。
bool hasCxxDtor = cls->hasCxxDtor();
// 获取对象的大小
size_t size = cls->instanceSize(extraBytes);
//....
//分配size内存空间
obj = (id)calloc(1, size);
//初始化isa
obj->initIsa(cls);
//是否有构造函数
if (cxxConstruct && hasCxxCtor) {
obj = _objc_constructOrFree(obj, cls);
}
return obj;
}

_class_createInstanceFromZone 方法中首先判断当前class或者superclass 是否有构造方法和析构方法。然后从类对象中获取要创建当前对象所需要的空间大小,紧接着就调用calloc分配内存,然后初始化Isa结构体。如果有构造函数的情况下还需要调用_objc_constructOrFree。_objc_constructOrFree最终会找到该类的C++构造方法,向它发送消息。

- (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;
}
2 对象的持有 retain

调用栈

对象的持有实际上是在对应的SideTable散列表中通过当前对象的地址作为散列值,找到存储当前对象引用计数的哈希表将引用计数增加1.我们下面看下整个调用过程:

--- retain
--- rootRetain
--- sidetable_retain
- (id)retain {
return ((id)self)->rootRetain();
}
ALWAYS_INLINE id 
objc_object::rootRetain()
{
return rootRetain(false, false);
}
ALWAYS_INLINE id 
objc_object::rootRetain(bool tryRetain, bool handleOverflow)
{
if (isTaggedPointer()) return (id)this;

bool sideTableLocked = false;
bool transcribeToSideTable = false;

isa_t oldisa;
isa_t newisa;

do {
transcribeToSideTable = false;
oldisa = LoadExclusive(&isa.bits);
newisa = oldisa;
if (slowpath(!newisa.nonpointer)) {
ClearExclusive(&isa.bits);
if (!tryRetain && sideTableLocked) sidetable_unlock();
if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;
else return sidetable_retain();
}
// don't check newisa.fast_rr; we already called any RR overrides
if (slowpath(tryRetain && newisa.deallocating)) {
ClearExclusive(&isa.bits);
if (!tryRetain && sideTableLocked) sidetable_unlock();
return nil;
}
uintptr_t carry;
newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry); // extra_rc++

if (slowpath(carry)) {
// newisa.extra_rc++ overflowed
if (!handleOverflow) {
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.
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 (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();
return (id)this;
}

这里最关键的是sidetable_retain,至于什么是SiteTable,以及对象引用计数的存储介绍可以查看 我准备的一个面试题:

谈谈iOS的内存管理方式的理解来理解如何查找散列表的过程。

id
objc_object::sidetable_retain()
{
#if SUPPORT_NONPOINTER_ISA
assert(!isa.nonpointer);
#endif
SideTable& table = SideTables()[this];

table.lock();
size_t& refcntStorage = table.refcnts[this];
if (! (refcntStorage & SIDE_TABLE_RC_PINNED)) {
refcntStorage += SIDE_TABLE_RC_ONE;
}
table.unlock();

return (id)this;
}
3 对象的释放 release

调用栈

对象的释放实际上是在对应的SideTable散列表中通过当前对象的地址作为散列值,找到存储当前对象引用计数的哈希表将引用计数减1.如果减去1后为0 那么就会调用dealloc释放对象:

--- release
--- rootRelease
--- sidetable_release
- (oneway void)release {
((id)self)->rootRelease();
}
objc_object::rootRelease()
{
return rootRelease(true, false);
}

ALWAYS_INLINE bool 
objc_object::rootRelease(bool performDealloc, bool handleUnderflow)
{
if (isTaggedPointer()) return false;

bool sideTableLocked = false;

isa_t oldisa;
isa_t newisa;

retry:
do {
oldisa = LoadExclusive(&isa.bits);
newisa = oldisa;
if (slowpath(!newisa.nonpointer)) {
ClearExclusive(&isa.bits);
if (sideTableLocked) sidetable_unlock();
return sidetable_release(performDealloc);
}
// don't check newisa.fast_rr; we already called any RR overrides
uintptr_t carry;
newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry); // extra_rc--
if (slowpath(carry)) {
// don't ClearExclusive()
goto underflow;
}
} while (slowpath(!StoreReleaseExclusive(&isa.bits,
oldisa.bits, newisa.bits)));

if (slowpath(sideTableLocked)) sidetable_unlock();
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 (!handleUnderflow) {
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.
goto retry;
}

// Try to remove some retain counts from the side table.
size_t borrowed = sidetable_subExtraRC_nolock(RC_HALF);

// To avoid races, has_sidetable_rc must remain set
// even if the side table count is now zero.

if (borrowed > 0) {
// Side table retain count decreased.
// Try to add them to the inline count.
newisa.extra_rc = borrowed - 1; // redo the original decrement too
bool stored = StoreReleaseExclusive(&isa.bits,
oldisa.bits, newisa.bits);
if (!stored) {
// 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.
isa_t oldisa2 = LoadExclusive(&isa.bits);
isa_t newisa2 = oldisa2;
if (newisa2.nonpointer) {
uintptr_t overflow;
newisa2.bits =
addc(newisa2.bits, RC_ONE * (borrowed-1), 0, &overflow);
if (!overflow) {
stored = StoreReleaseExclusive(&isa.bits, oldisa2.bits,
newisa2.bits);
}
}
}

if (!stored) {
// Inline update failed.
// Put the retains back in the side table.
sidetable_addExtraRC_nolock(borrowed);
goto retry;
}

// Decrement successful after borrowing from side table.
// This decrement cannot be the deallocating decrement - the side
// table lock and has_sidetable_rc bit ensure that if everyone
// else tried to -release while we worked, the last one would block.
sidetable_unlock();
return false;
}
else {
// Side table is empty after all. Fall-through to the dealloc path.
}
}

// Really deallocate.

if (slowpath(newisa.deallocating)) {
ClearExclusive(&isa.bits);
if (sideTableLocked) sidetable_unlock();
return overrelease_error();
// does not actually return
}
newisa.deallocating = true;
if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;

if (slowpath(sideTableLocked)) sidetable_unlock();

__sync_synchronize();
if (performDealloc) {
((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
}
return true;
}

uintptr_t
objc_object::sidetable_release(bool performDealloc)
{
#if SUPPORT_NONPOINTER_ISA
assert(!isa.nonpointer);
#endif
SideTable& table = SideTables()[this];

bool do_dealloc = false;

table.lock();
//在散列表中找到当前对象对应的引用计数
RefcountMap::iterator it = table.refcnts.find(this);
if (it == table.refcnts.end()) {
//没找到,标记为已经销毁
do_dealloc = true;
table.refcnts[this] = SIDE_TABLE_DEALLOCATING;
} else if (it->second < SIDE_TABLE_DEALLOCATING) {
// SIDE_TABLE_WEAKLY_REFERENCED may be set. Don't change it.
do_dealloc = true;
it->second |= SIDE_TABLE_DEALLOCATING;
} else if (! (it->second & SIDE_TABLE_RC_PINNED)) {
//引用计数减去1
it->second -= SIDE_TABLE_RC_ONE;
}
table.unlock();
//释放已经释放
if (do_dealloc && performDealloc) {
//调用dealloc
((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
}
return do_dealloc;
}
4 对象的销毁 dealloc

在介绍对象释放的最后有提到在某个对象的引用计数减为0的时候会调用对象的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);
}
}
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;
}

这里需要注意的是rootDealloc方法,它会先判断是否是taggpointer,如果是的话就直接返回,因为这种对象不需要释放内存,它的值就存放在指针中,这部分相关的内容可以查看
谈谈iOS的内存管理方式的理解,紧接会判断当前对象的具体情况,如果当前对象是Nonpointer_ISA类型,那么要看相应的标识位,判断是否是弱引用,是否有关联对象,是否有C++析构函数,是否用到了散列表?如果都没有的话就直接free对象。否则通过object_dispose进行后续处理:

相关的具体处理最终在objc_destructInstance中实现。大家可以看下该方法的注释:

/***********************************************************************
* objc_destructInstance
* Destroys an instance without freeing memory.
* Calls C++ destructors.
* Calls ARC ivar cleanup.
* Removes associative references.
* Returns `obj`. Does nothing if `obj` is nil.
**********************************************************************/

这里面重点针对是否有C++析构函数,是否有关联对象,是否有弱引用进行了对应的处理,我们接下来重点对这部分进行介绍:

调用析构函数是否对象

void object_cxxDestruct(id obj)
{
if (!obj) return;
if (obj->isTaggedPointer()) return;
object_cxxDestructFromClass(obj, obj->ISA());
}


static void object_cxxDestructFromClass(id obj, Class cls)
{
void (*dtor)(id);

// Call cls's dtor first, then superclasses's dtors.

for ( ; cls; cls = cls->superclass) {
if (!cls->hasCxxDtor()) return;
dtor = (void(*)(id))
lookupMethodInClassAndLoadCache(cls, SEL_cxx_destruct);
if (dtor != (void(*)(id))_objc_msgForward_impcache) {
if (PrintCxxCtors) {
_objc_inform("CXX: calling C++ destructors for class %s",
cls->nameForLogging());
}
(*dtor)(obj);
}
}
}

实际上就是在类的方法列表中查找对应的析构函数,然后对其进行调用。如果针对这部分想要细致了解大家可以查看ARC下dealloc过程及.cxx_destruct的探究这篇博客。

ARC下在编译器插入的.cxx_desctruct方法中会完成成员变量的自动释放。而对象的实例变量的销毁发生在根类[NSObject dealloc]方法中。

关联属性移除

关于关联对象大家可以查看对应的博客,它其实是由全局AssociationsManager进行管理的,在这里会找到对应的关联对象,然后添加到elements,然后对elements里面的对象调用ReleaseValue进行移除。这样和这个对象相关的关联对象就从AssociationsManager中移除了。

void _object_remove_assocations(id object) {
vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
if (associations.size() == 0) return;
disguised_ptr_t disguised_object = DISGUISE(object);
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// copy all of the associations that need to be removed.
ObjectAssociationMap *refs = i->second;
for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
elements.push_back(j->second);
}
// remove the secondary table.
delete refs;
associations.erase(i);
}
}
// the calls to releaseValue() happen outside of the lock.
for_each(elements.begin(), elements.end(), ReleaseValue());
}

清除剩余标识位

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.
// non-pointer isa 类型的对象。这种对象之所以要和普通对象区分开是因为它的弱引用标记以及是否使用散列表信息都存在“指针”中,所以要分开处理,但是处理的流程都是一样的,都是查看是否被弱引用指针指向,如果有则将这些设置为nil。紧接着从散列表中清除引用计数。最后将弱引用项从弱引用表中移除。
clearDeallocating_slow();
}

assert(!sidetable_present());
}

clearDeallocating 中实际上是用于处理剩余的标识位,这里分成是否是non-pointer isa:
non-pointer isa 对象之所以要和普通对象区分开是因为它的弱引用标记以及是否使用散列表信息都存在“指针”中,所以要分开处理,但是处理的流程都是一样的,都是查看是否被弱引用指针指向,如果有则将这些设置为nil。紧接着从散列表中清除引用计数。最后将弱引用项从弱引用表中移除。

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) {
//如果是的话则将对应对象对它的引用设置为nil
weak_clear_no_lock(&table.weak_table, (id)this);
}
//清除引用计数
table.refcnts.erase(it);
}
table.unlock();
}

sidetable_clearDeallocating 就做了三件事情:

  1. 找到对应的散列表
  2. 查看弱引用标记,是否是弱引用,如果是的话则将其他对象对它的引用置为nil,并将弱引用标志从SideTable的弱引用表中移除。
  3. 将该对象对应的引用计数表从SideTables中移除
void 
weak_clear_no_lock(weak_table_t *weak_table, id referent_id)
{
objc_object *referent = (objc_object *)referent_id;

//找到对应的弱引用项入口
weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
if (entry == nil) {
return;
}

// zero out references
weak_referrer_t *referrers;
size_t count;

if (entry->out_of_line()) {
referrers = entry->referrers;
count = TABLE_SIZE(entry);
}
else {
referrers = entry->inline_referrers;
count = WEAK_INLINE_COUNT;
}

for (size_t i = 0; i < count; ++i) {
objc_object **referrer = referrers[i];
if (referrer) {
if (*referrer == referent) {
//将其他对象对他的引用设置为nil
*referrer = nil;
}
else if (*referrer) {
_objc_inform("__weak variable at %p holds %p instead of %p. "
"This is probably incorrect use of "
"objc_storeWeak() and objc_loadWeak(). "
"Break on objc_weak_error to debug.\n",
referrer, (void*)*referrer, (void*)referent);
objc_weak_error();
}
}
}
//移除该项
weak_entry_remove(weak_table, entry);
}
5 Weak指针原理

__weak 修饰的变量有两大特点:

  • 不会增加指向对象的引用计数 (规避循环引用)
  • 指向对象释放后,变量会自动置 nil (规避野指针访问错误)

大体的实现思路如下:

使用__weak 修饰的变量时,runtime 会生成对应的 weak_entry 结构放入 weak_table 中,以赋值对象地址生成的 hash 值为 key,以包装 __weak 修饰的指针变量地址的 entry 为 value,当赋值对象释放时,runtime 会在目标对象的 dealloc 处理过程中,以对象地址(self)为 key 去 weak_table 查找 entry ,置空 entry 指向的的所有对象指针。weak_entry 使用数组保存指针变量地址,当地址数量小于4的时候,这个数组就是个普通的内置数组,在地址数量大于4的时候,这个数组就会扩充成一个 hash table。

我们接下来看下这部分源码:

当我们声明一个指针变量的时候就会调用objc_initWeak方法:

id
objc_initWeak(id *location, id newObj)
{
if (!newObj) {
*location = nil;
return nil;
}

return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
(location, (objc_object*)newObj);
}

weak 对象的存储是通过storeWeak完成的,在看storeWeak代码之前我们先来看下weak_table_t

struct weak_table_t {
weak_entry_t *weak_entries;
size_t num_entries;
uintptr_t mask;
uintptr_t max_hash_displacement;
};

weak_table_t 中存储的每个weak_entry_t是以被引用对象地址为hash Code 进行存储的,这个没啥好说的,我们来看下weak_entry_t。

struct weak_entry_t {
DisguisedPtr<objc_object> referent;
union {
struct {
weak_referrer_t *referrers;
uintptr_t out_of_line_ness : 2;
uintptr_t num_refs : PTR_MINUS_2;
uintptr_t mask;
uintptr_t max_hash_displacement;
};
struct {
// out_of_line_ness field is low bits of inline_referrers[1]
weak_referrer_t inline_referrers[WEAK_INLINE_COUNT];
};
};

bool out_of_line() {
return (out_of_line_ness == REFERRERS_OUT_OF_LINE);
}

weak_entry_t& operator=(const weak_entry_t& other) {
memcpy(this, &other, sizeof(other));
return *this;
}

weak_entry_t(objc_object *newReferent, objc_object **newReferrer)
: referent(newReferent)
{
inline_referrers[0] = newReferrer;
for (int i = 1; i < WEAK_INLINE_COUNT; i++) {
inline_referrers[i] = nil;
}
}
};

weak_entry_t 里面包含了一个union,里面有两个结构体,其中 WEAK_INLINE_COUNT 宏为 4 ,也就是说在初始状态下,这个 union 的空间有 weak_referrer_t inline_referrers[4] 这么大,当 entry 保存指针变量地址的个数不大于 4 个时,我们就可以直接使用 inline_referrers 数组,这样写的话,访问更加快速便捷。但是如果超过4个的话则使用哈希表来存储,这样会加速查找的速度。

下面是weak相关数据结构的大体示意图:

referent: 是内存上的weak对象
referrers: 是指向weak对象的所有变量

有了上面的存储结构的介绍对storeWeak会有更清晰的理解

在设置新的关联前,如果 __weak 修饰的指针变量已经关联了其他对象,那么此函数会先解除旧关联,再设置新的。如果 newObjc 是 nil,那么只会进行解除关联以及指针置 nil 操作,objc_destroyWeak 就以这种方式调用 storeWeak 来执行销毁动作,这个后面会介绍。

template <HaveOld haveOld, HaveNew haveNew,
CrashIfDeallocating crashIfDeallocating>
static id
storeWeak(id *location, objc_object *newObj)
{
assert(haveOld || haveNew);
if (!haveNew) assert(newObj == nil);

Class previouslyInitializedClass = nil;
id oldObj;
SideTable *oldTable;
SideTable *newTable;

//..........

// Clean up old value, if any.
if (haveOld) {
//__weak 修饰的指针变量已经指向过某对象
// 需要把这个对象和此指针变量的关联断开
weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
}

// Assign new value, if any.
if (haveNew) {
// 关联新对象和 __weak 修饰的指针变量
newObj = (objc_object *)
weak_register_no_lock(&newTable->weak_table, (id)newObj, location,
crashIfDeallocating);
// weak_register_no_lock returns nil if weak store should be rejected

// Set is-weakly-referenced bit in refcount table.
if (newObj && !newObj->isTaggedPointer()) {
// 设置 isa 指针的 weakly_referenced 位 / sidetable 中的 SIDE_TABLE_WEAKLY_REFERENCED 位
// 标记此对象被 __weak 修饰的指针变量指向了,dealloc 时可以加速置 nil 处理
newObj->setWeaklyReferenced_nolock();
}
// 设置 __weak 修饰的指针变量的值为 newObj
// Do not set *location anywhere else. That would introduce a race.
*location = (id)newObj;
}
else {
// No new value. The storage is not changed.
}

SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);

return (id)newObj;
}
id 
weak_register_no_lock(weak_table_t *weak_table, id referent_id,
id *referrer_id, bool crashIfDeallocating)
{
objc_object *referent = (objc_object *)referent_id;
objc_object **referrer = (objc_object **)referrer_id;

if (!referent || referent->isTaggedPointer()) return referent_id;

// ensure that the referenced object is viable
bool deallocating;
if (!referent->ISA()->hasCustomRR()) {
deallocating = referent->rootIsDeallocating();
}
else {
BOOL (*allowsWeakReference)(objc_object *, SEL) =
(BOOL(*)(objc_object *, SEL))
object_getMethodImplementation((id)referent,
SEL_allowsWeakReference);
if ((IMP)allowsWeakReference == _objc_msgForward) {
return nil;
}
deallocating =
! (*allowsWeakReference)(referent, SEL_allowsWeakReference);
}

if (deallocating) {
if (crashIfDeallocating) {
_objc_fatal("Cannot form weak reference to instance (%p) of "
"class %s. It is possible that this object was "
"over-released, or is in the process of deallocation.",
(void*)referent, object_getClassName((id)referent));
} else {
return nil;
}
}

// now remember it and where it is being stored
weak_entry_t *entry;
if ((entry = weak_entry_for_referent(weak_table, referent))) {
append_referrer(entry, referrer);
}
else {
weak_entry_t new_entry(referent, referrer);
weak_grow_maybe(weak_table);
weak_entry_insert(weak_table, &new_entry);
}

// Do not set *referrer. objc_storeWeak() requires that the
// value not change.

return referent_id;
}

最关键的代码在weak_register_no_lock 中,它会判断某个referent的weak_entry是否存在,如果存在则直接在这个weak_entry中添加referrer,否则新建一个weak_entry,加到weak_table中。

weak_entry_t *entry;
if ((entry = weak_entry_for_referent(weak_table, referent))) {
append_referrer(entry, referrer);
}
else {
weak_entry_t new_entry(referent, referrer);
weak_grow_maybe(weak_table);
weak_entry_insert(weak_table, &new_entry);
}

销毁弱引用对象

void
objc_destroyWeak(id *location)
{
(void)storeWeak<DoHaveOld, DontHaveNew, DontCrashIfDeallocating>
(location, nil);
}

销毁弱引用对象和创建弱引用对象入口实际上是一样的,都是storeWeak,只不过这里传入的值为nil。由于传入的是非 nil 新值,storeWeak会删除而不会新建关联信息。

void weak_unregister_no_lock(weak_table_t *weak_table, id referent_id, 
id *referrer_id)
{
objc_object *referent = (objc_object *)referent_id;
objc_object **referrer = (objc_object **)referrer_id;

weak_entry_t *entry;

if (!referent) return;
// 根据对象地址获取 entry
if ((entry = weak_entry_for_referent(weak_table, referent))) {
// 移除 entry 中值为 referrer 的指针变量地址
remove_referrer(entry, referrer);
bool empty = true;
// entry 中是否有关联的指针变量地址
if (entry->out_of_line() && entry->num_refs != 0) {
empty = false;
}
else {
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
if (entry->inline_referrers[i]) {
empty = false;
break;
}
}
}

if (empty) {
// 如果 entry 是空的话,就从 weak_table 中移除掉
weak_entry_remove(weak_table, entry);
}
}
}
Contents
  1. 1. 1 对象的创建 alloc/init:
  2. 2. 2 对象的持有 retain
  3. 3. 3 对象的释放 release
  4. 4. 4 对象的销毁 dealloc
  5. 5. 5 Weak指针原理