iOS - weak指针原理
2020-10-23 本文已影响0人
米拉_Recorder
weak指针原理,即weak指针是怎么样在对象销毁的时候被置为nil的。这就要看runtime源码在对象销毁的时候,都做了些什么。
弱引用需要借助于运行时Runtime,是在程序运行时监控对象销毁把这个对象的弱引用清除掉。
原理是将弱引用相关的东西存储到一个弱引用表里,当这个对象要销毁的时候,就取出这个对象对应的弱引用表,把这个弱引用表里对应的弱引用都清除掉。弱引用表也是采取哈希表的存储。一步一步找到weak_entry_t,然后从表中移除。
底层实现源码:
rootDealloc()
根据这个代码也可以看出,优化的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();
}
// 优化过的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);
}
// 如果弱引用清除后,如果发现还有sideTable也会将引用计数表里面的东西擦除掉,因为当前对象需要销毁。
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
引用计数器的存放位置
weak_table_t weak_table
弱引用的哈希表
struct SideTable {
spinlock_t slock;
//哈希表用来存储引用计数。当前对象的地址值作为key,来取出对应的引用计数。
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);
};