ARC&MRC主要与alloc
,retain
,realese
,retainCount
,autorealese
,dealloc
这些方法的调用有关。
ARC:是LLVM和Runtime配合的结果,ARC中禁止使用手动调用reatain/release/retainCount/dealloc,ARC新加了weak,strong属性关键字。
这里穿插一个问题:
FXTank *p = [[FXTank alloc] init];
NSLog(@"%lu", (unsigned long)[p retainCount]);
这段代码输出多少?(首先需要知道引用计数存在isa.bits. extra_rc或者散列表)
打印如下:为啥输出1,从源码来找答案:
- (NSUInteger)retainCount {
return ((id)self)->rootRetainCount();
}
进入rootRetainCount
方法:
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();
}
总结:从这里可以看到,uintptr_t rc = 1 + bits.extra_rc;
引用计数手动加1(其中bit.extra_rc
打印为nil
),如果bits.has_sidetable_rc
哈希表没有引用计数,就直接返回1,所以前面的的答案是1,至于这里为啥为1,原因是如果为0,就直接析构了释放了。
下面继续测试:
FXTank *p = [[FXTank alloc] init];
[p retain]; //这里的引用计数+1
NSLog(@"%lu", (unsigned long)[p retainCount]);
打印如下:
分析:
retain
是加到extrac
,因为本来retainCount
就会1+extra_rc
,这里extra_rc
由加了1,所以输出结果为2。查看
retain
源码:
// Replaced by ObjectAlloc
- (id)retain {
return ((id)self)->rootRetain();
}
进入rootRetain
:
// Base retain implementation, ignoring overrides.
// This does not check isa.fast_rr; if there is an RR override then
// it was already called and it chose to call [super retain].
//
// tryRetain=true is the -_tryRetain path.
// handleOverflow=false is the frameless fast path.
// handleOverflow=true is the framed slow path including overflow to side table
// The code is structured this way to prevent duplication.
ALWAYS_INLINE id
objc_object::rootRetain()
{
return rootRetain(false, false);
}
进入rootRetain:
ALWAYS_INLINE id
objc_object::rootRetain(bool tryRetain, bool handleOverflow)
{
if (isTaggedPointer()) return (id)this; //TaggedPointer是不会走下面retain,release,不会使用到引用计数的加减操作
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;
}
分析:首先整体是一个do-while循环,这里while判断是引用计数是否储备完整,如果没有储备完整,就一直在do循环里面处理ARC的引用计数。newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry); // extra_rc++
这段代码就是用来引用计数+1的,RC_ONE(宏定义 )就是1,# define RC_ONE (1ULL<<56)
,为什么是左移动56位呢?其实在上篇博客里面提到过,在ISA_BITFIELD
宏定义里面使用了联合体,其中uintptr_t extra_rc : 8
的意思就是代表占用了64位中的后8位,所以这里的1要加到后8位中,左移56位代表的就是后8位中的1。
newisa.extra_rc = RC_HALF;
newisa.has_sidetable_rc = true; //标记需要存哈希表
sidetable_addExtraRC_nolock(RC_HALF);
这段代码的意思是,如果extra_rc
存满了,就取出一半存到SideTable
哈希表里面的RefcountMap refcnts;
引用计数map
里面。
注意:其中# define RC_HALF (1ULL<<7)
为8位的一半,差一个最高位就是二进制差一半。
进入sidetable_addExtraRC_nolock
方法
// Move some retain counts to the side table from the isa field.
// Returns true if the object is now pinned.
bool
objc_object::sidetable_addExtraRC_nolock(size_t delta_rc)
{
assert(isa.nonpointer);
SideTable& table = SideTables()[this]; //取出响应的散列表
size_t& refcntStorage = table.refcnts[this];
size_t oldRefcnt = refcntStorage;
// isa-side bits should not be set here
assert((oldRefcnt & SIDE_TABLE_DEALLOCATING) == 0);
assert((oldRefcnt & SIDE_TABLE_WEAKLY_REFERENCED) == 0);
if (oldRefcnt & SIDE_TABLE_RC_PINNED) return true;
uintptr_t carry;
size_t newRefcnt =
addc(oldRefcnt, delta_rc << SIDE_TABLE_RC_SHIFT, 0, &carry); //引用计数+1
if (carry) {
refcntStorage =
SIDE_TABLE_RC_PINNED | (oldRefcnt & SIDE_TABLE_FLAG_MASK);
return true;
}
else {
refcntStorage = newRefcnt;
return false;
}
}
分析:从refcnts
中取出散列表,addc
方法引用计数+1。
注意这里的SIDE_TABLE_RC_SHIFT
为2,因为这里是前两位做其他用途,所以第2位开始,和前面有所不同。(如果散列表也存满了,就会处理引用计数传存储,这里不再深入探讨了)
总结:回到最开始的引用计数打印,就很明确了,先取bits. extra_rc里面的引用计数,然后判断has_sidetable_rc
里面是否有引用计数,最后相加返回最终的引用计数个数。
接下来我们在试下下面这段代码:
FXTank *p = [[FXTank alloc] init];
[p retain];
NSLog(@"%lu", (unsigned long)[p retainCount]);
[p release];
NSLog(@"%lu", (unsigned long)[p retainCount]);
下面来看下release
方法源码:
// Replaced by ObjectAlloc
- (oneway void)release {
((id)self)->rootRelease();
}
进入rootRelease
方法:
// Base release implementation, ignoring overrides.
// Does not call -dealloc.
// Returns true if the object should now be deallocated.
// This does not check isa.fast_rr; if there is an RR override then
// it was already called and it chose to call [super release].
//
// handleUnderflow=false is the frameless fast path.
// handleUnderflow=true is the framed slow path including side table borrow
// The code is structured this way to prevent duplication.
ALWAYS_INLINE bool
objc_object::rootRelease()
{
return rootRelease(true, false);
}
进入rootRelease方法:
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;
}
分析:前面引用计数-1和+1类似,这里也有array
溢出,只不过是下溢出,就是减到0继续减,下溢出之后会调用underflow
,判断SideTable
是否有引用计数,如果有就从散列表里面borrowed
借用RC_HALF-1
(也就是RC_HALF一半),然后在调用retry
递归继续引用计数-1,如果散列表里面也没有了,调用析构方法,给this
对象发SEL_dealloc
消息。
注意:这里有一个问题,在对象刚创建的时候引用计数为0,但是不会走析构函数,只有在调用
release
函数把引用计数减到0的时候才会调用析构函数,也就是说引用计数为0是调用析构函数必要条件,但不是充分条件,必须要有一个release
触发的过程。
引用计数总结:isa是一个NONPOINTER_ISA
,他是按位存储的。我们的引用计数存储是有两个位置的,第一个是在isa.bits.extra_rc
,第二个是在散列表里面,retain
是会进行extra_rc+1
返回的,extra_rc
只有8给位置,会发送carry
上溢出,就会吧一半的内容放到散列表里面,如果散列表也满了,会调整容量结构。release
会进行extra_rc-1
,如果extra_rc
为0,就会发生下溢出,下溢出会判断散列表,然后借用一半存到extra_rc
里面,如果散列表里面也没有,就会调用这个对象的析构函数。
疑问:这里有一个问题就是为什么要把extra_rc中的一半放到散列表里面,而不是全部?原因是不能过于平凡的操作散列表,如果全部都放到散列表里面,每次release的时候都会访问散列表,虽然extra_rc操作简单了,但是散列表的操作增多了,这样对于性能不是很好(感觉一半的话有点像二分法的思想,玄学)。
接下来我们看下Dealloc,首先是应该在Dealloc函数里面做什么事情?(底层实现)
- free函数,释放相关对象。
- weak引用计数表需要做处理。
- 关联对象associated对象需要释放。
首先找到源码:
// Replaced by NSZombies
- (void)dealloc {
_objc_rootDealloc(self);
}
进入_objc_rootDealloc
:
void
_objc_rootDealloc(id obj)
{
assert(obj);
obj->rootDealloc();
}
继续进入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);
}
}
分析:首先判断是否是nonpointer
(小类型特殊处理),weakly_referenced
(弱引用),has_assoc
(关联对象),has_cxx_dtor
(c++),has_sidetable_rc
(散列表引用计数),如果不是直接free
,否则走一般对象释放流程object_dispose
。
继续进入object_dispose
:
/***********************************************************************
* object_dispose
* fixme
* Locking: none
**********************************************************************/
id
object_dispose(id obj)
{
if (!obj) return nil;
objc_destructInstance(obj); //处理对象,为释放做准备
free(obj); //处理完成后释放
return nil;
}
分析:主要是objc_destructInstance
为释放做准备,然后直接free释放对象。
继续进入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.
**********************************************************************/
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;
}
分析:如果有c++的析构函数就先调用c++析构,如果有关联对象的就移除。
然后进入clearDeallocating方法:
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());
}
继续进入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();
}
分析:如果it不再table.refcnts.end,就是说it不是最后一个的话,就一个一个从头开始往后遍历移除,因为对象已经不存在了,所以然后就是table.refcnts.erase(it);
引用计数移除。
进入weak_clear_no_lock方法:
/**
* Called by dealloc; nils out all weak pointers that point to the
* provided object so that they can no longer be used.
*
* @param weak_table
* @param referent The object being deallocated.
*/
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) {
/// XXX shouldn't happen, but does with mismatched CF/objc
//printf("XXX no entry for clear deallocating %p\n", referent);
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) {
*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);
}
分析:首先拿到weaktable
里面的entry
,然后再取出entry
里面的referrers
,然后for循环遍历referrers
,如果referrer
存在,直接置为nil
,回收内存。(这里解释了经常说的weak
一旦被释放,就会职位nil
,不存在所谓的循环引用,因为它是一个nil
对象),因为referrers
已经空了,所以entry
存在的意义也就没有了,所以调用weak_entry_remove
(方法内部还有移除容量等)移除entry
。
析构总结:(总流程图)
后面是补充的weak原理:
weak原理:
NSObject *obj = [[NSObject alloc] init];
id __weak obj2 = obj;
weak修饰变量在断点处可以看到调用objc_initWeak方法,如下:
下面我们在源码中查看objc_initWeak这个方法:
/**
* Initialize a fresh weak pointer to some object location.
* It would be used for code like:
*
* (The nil case)
* __weak id weakPtr;
* (The non-nil case)
* NSObject *o = ...;
* __weak id weakPtr = o;
*
* This function IS NOT thread-safe with respect to concurrent
* modifications to the weak variable. (Concurrent weak clear is safe.)
*
* @param location Address of __weak ptr.
* @param newObj Object ptr.
*/
id
objc_initWeak(id *location, id newObj)
{
if (!newObj) {
*location = nil;
return nil;
}
return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
(location, (objc_object*)newObj);
}
继续进入storeWeak:
// Update a weak variable.
// If HaveOld is true, the variable has an existing value
// that needs to be cleaned up. This value might be nil.
// If HaveNew is true, there is a new value that needs to be
// assigned into the variable. This value might be nil.
// If CrashIfDeallocating is true, the process is halted if newObj is
// deallocating or newObj's class does not support weak references.
// If CrashIfDeallocating is false, nil is stored instead.
enum CrashIfDeallocating {
DontCrashIfDeallocating = false, DoCrashIfDeallocating = true
};
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;
// Acquire locks for old and new values.
// Order by lock address to prevent lock ordering problems.
// Retry if the old value changes underneath us.
retry:
if (haveOld) {
oldObj = *location;
oldTable = &SideTables()[oldObj];
} else {
oldTable = nil;
}
if (haveNew) {
newTable = &SideTables()[newObj];
} else {
newTable = nil;
}
SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);
if (haveOld && *location != oldObj) {
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
goto retry;
}
// Prevent a deadlock between the weak reference machinery
// and the +initialize machinery by ensuring that no
// weakly-referenced object has an un-+initialized isa.
if (haveNew && newObj) {
Class cls = newObj->getIsa();
if (cls != previouslyInitializedClass &&
!((objc_class *)cls)->isInitialized())
{
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
_class_initialize(_class_getNonMetaClass(cls, (id)newObj));
// If this class is finished with +initialize then we're good.
// If this class is still running +initialize on this thread
// (i.e. +initialize called storeWeak on an instance of itself)
// then we may proceed but it will appear initializing and
// not yet initialized to the check above.
// Instead set previouslyInitializedClass to recognize it on retry.
previouslyInitializedClass = cls;
goto retry;
}
}
// Clean up old value, if any.
if (haveOld) {
weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
}
// Assign new value, if any.
if (haveNew) {
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()) {
newObj->setWeaklyReferenced_nolock();
}
// 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;
}
分析:这里的重点是,在添加新的weak
对象的时候,先要判断是否存在旧值,如果存在旧要先移除旧的,然后调用weak_register_no_lock
添加新的weak
对象,下面进入weak_register_no_lock
方法继续分析源码。
进入weak_register_no_lock
方法:
/**
* Registers a new (object, weak pointer) pair. Creates a new weak
* object entry if it does not exist.
*
* @param weak_table The global weak table.
* @param referent The object pointed to by the weak reference.
* @param referrer The weak pointer address.
*/
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); //创建新entry
weak_grow_maybe(weak_table); //空间是否要扩容
weak_entry_insert(weak_table, &new_entry); //插入新entry
}
// Do not set *referrer. objc_storeWeak() requires that the
// value not change.
return referent_id;
}
分析:这里有两个逻辑,第一个是如果有entry就调用append_referrer加入到weak_table里面,如果weak表里面没有entry就创建新weak_entry_t的,然后加入到weak_table里面。
这
继续进入weak_entry_for_referent
:
/**
* Return the weak reference table entry for the given referent.
* If there is no entry for referent, return NULL.
* Performs a lookup.
*
* @param weak_table
* @param referent The object. Must not be nil.
*
* @return The table of weak referrers to this object.
*/
static weak_entry_t *
weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent)
{
assert(referent);
weak_entry_t *weak_entries = weak_table->weak_entries;
if (!weak_entries) return nil;
size_t begin = hash_pointer(referent) & weak_table->mask;
size_t index = begin;
size_t hash_displacement = 0;
while (weak_table->weak_entries[index].referent != referent) {
index = (index+1) & weak_table->mask;
if (index == begin) bad_weak_table(weak_table->weak_entries);
hash_displacement++;
if (hash_displacement > weak_table->max_hash_displacement) {
return nil;
}
}
return &weak_table->weak_entries[index];
}
分析:这里的结构是一张哈希表,while循环一直找到和referent的key值不一样的index然后从返回 &weak_table->weak_entries[index] (entry地址),返回的&entry就会调用下面的append_referrer方法添加到weak_table里面。
/**
* Add the given referrer to set of weak pointers in this entry.
* Does not perform duplicate checking (b/c weak pointers are never
* added to a set twice).
*
* @param entry The entry holding the set of weak pointers.
* @param new_referrer The new weak pointer to be added.
*/
static void append_referrer(weak_entry_t *entry, objc_object **new_referrer)
{
if (! entry->out_of_line()) {
// Try to insert inline.
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
if (entry->inline_referrers[i] == nil) {
entry->inline_referrers[i] = new_referrer;
return;
}
}
// Couldn't insert inline. Allocate out of line.
weak_referrer_t *new_referrers = (weak_referrer_t *)
calloc(WEAK_INLINE_COUNT, sizeof(weak_referrer_t));
// This constructed table is invalid, but grow_refs_and_insert
// will fix it and rehash it.
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
new_referrers[i] = entry->inline_referrers[i];
}
entry->referrers = new_referrers;
entry->num_refs = WEAK_INLINE_COUNT;
entry->out_of_line_ness = REFERRERS_OUT_OF_LINE;
entry->mask = WEAK_INLINE_COUNT-1;
entry->max_hash_displacement = 0;
}
assert(entry->out_of_line());
if (entry->num_refs >= TABLE_SIZE(entry) * 3/4) {
return grow_refs_and_insert(entry, new_referrer);
}
size_t begin = w_hash_pointer(new_referrer) & (entry->mask);
size_t index = begin;
size_t hash_displacement = 0;
while (entry->referrers[index] != nil) {
hash_displacement++;
index = (index+1) & entry->mask;
if (index == begin) bad_weak_table(entry);
}
if (hash_displacement > entry->max_hash_displacement) {
entry->max_hash_displacement = hash_displacement;
}
weak_referrer_t &ref = entry->referrers[index];
ref = new_referrer;
entry->num_refs++;
}
weak总结下:
结构流程:
sidetabless -> sidetable -> 弱引用表
weakTable -> entry - >数组 ->弱引用对象指针
存储流程:
- weak_entry *weak_entries 取出entry
- entry->inline_referres[i] = new_referrer
- new_referrers[i] = entry->inline_referrers[i]
- entry->referrers = new_referrers
注意:这里weak是没有操作引用计数的。
流程图示:
网友评论