weak指針原理跷车,即weak指針是怎么樣在對象銷毀的時候被置為nil的。這就要看runtime源碼在對象銷毀的時候牵现,都做了些什么。
弱引用需要借助于運行時Runtime邀桑,是在程序運行時監(jiān)控對象銷毀把這個對象的弱引用清除掉瞎疼。
原理是將弱引用相關的東西存儲到一個弱引用表里,當這個對象要銷毀的時候,就取出這個對象對應的弱引用表,把這個弱引用表里對應的弱引用都清除掉路召。弱引用表也是采取哈希表的存儲蹬敲。一步一步找到weak_entry_t,然后從表中移除。
底層實現(xiàn)源碼:
rootDealloc()
根據(jù)這個代碼也可以看出,優(yōu)化的isa的結構,當沒有這些東西的時候腻异,為什么釋放的更快的原因。
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);
}
}
object_dispose()
object_dispose(id obj)
{
if (!obj) return nil;
objc_destructInstance(obj);
free(obj);
return nil;
}
objc_destructInstance()
void *objc_destructInstance(id obj)
{
if (obj) {
Class isa = obj->getIsa();
if (isa->hasCxxDtor()) {
object_cxxDestruct(obj);
}
if (isa->instancesHaveAssociatedObjects()) {
_object_remove_assocations(obj);
}
// 這一步就是將指向對象的weak指針清空
objc_clear_deallocating(obj);
}
return obj;
}
objc_clear_deallocating()
objc_clear_deallocating(id obj)
{
ASSERT(obj);
if (obj->isTaggedPointer()) return;
obj->clearDeallocating();
}
clearDeallocating()
objc_object::clearDeallocating()
{
// 普通的isa指針
if (slowpath(!isa.nonpointer)) {
// Slow path for raw pointer isa.
sidetable_clearDeallocating();
}
// 優(yōu)化過的isa指針
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()
objc_object::clearDeallocating_slow()
{
ASSERT(isa.nonpointer && (isa.weakly_referenced || isa.has_sidetable_rc));
SideTable& table = SideTables()[this];
table.lock();
if (isa.weakly_referenced) {
// 拿出sideTable里的weakTable
weak_clear_no_lock(&table.weak_table, (id)this);
}
// 如果弱引用清除后这揣,如果發(fā)現(xiàn)還有sideTable也會將引用計數(shù)表里面的東西擦除掉悔常,因為當前對象需要銷毀。
if (isa.has_sidetable_rc) {
table.refcnts.erase(this);
}
table.unlock();
}
weak_clear_no_lock()
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();
}
}
}
// 找到entry后從哈希表中移除
weak_entry_remove(weak_table, entry);
}
weak_entry_for_referent()
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;
// 利用referent這個地址值 & weak_table->mask 得出一個索引给赞,這就是哈希表找索引机打。取出對應的東西。
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];
}
SideTable結構
RefcountMap refcnts
引用計數(shù)器的存放位置
weak_table_t weak_table
弱引用的哈希表
struct SideTable {
spinlock_t slock;
//哈希表用來存儲引用計數(shù)片迅。當前對象的地址值作為key残邀,來取出對應的引用計數(shù)。
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);
};