一、weak_table_t
和 weak_entry_t
的结构
源码:
/*
The weak table is a hash table governed by a single spin lock.
An allocated blob of memory, most often an object, but under GC any such
allocation, may have its address stored in a __weak marked storage location
through use of compiler generated write-barriers or hand coded uses of the
register weak primitive. Associated with the registration can be a callback
block for the case when one of the allocated chunks of memory is reclaimed.
The table is hashed on the address of the allocated memory. When __weak
marked memory changes its reference, we count on the fact that we can still
see its previous reference.
So, in the hash table, indexed by the weakly referenced item, is a list of
all locations where this address is currently being stored.
For ARC, we also keep track of whether an arbitrary object is being
deallocated by briefly placing it in the table just prior to invoking
dealloc, and removing it via objc_clear_deallocating just prior to memory
reclamation.
*/
// The address of a __weak variable.
// These pointers are stored disguised so memory analysis tools
// don't see lots of interior pointers from the weak table into objects.
typedef DisguisedPtr<objc_object *> weak_referrer_t;
#if __LP64__
#define PTR_MINUS_2 62
#else
#define PTR_MINUS_2 30
#endif
/**
* The internal structure stored in the weak references table.
* It maintains and stores
* a hash set of weak references pointing to an object.
* If out_of_line_ness != REFERRERS_OUT_OF_LINE then the set
* is instead a small inline array.
*/
#define WEAK_INLINE_COUNT 4
// out_of_line_ness field overlaps with the low two bits of inline_referrers[1].
// inline_referrers[1] is a DisguisedPtr of a pointer-aligned address.
// The low two bits of a pointer-aligned DisguisedPtr will always be 0b00
// (disguised nil or 0x80..00) or 0b11 (any other address).
// Therefore out_of_line_ness == 0b10 is used to mark the out-of-line state.
#define REFERRERS_OUT_OF_LINE 2
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;
}
}
};
/**
* The global weak references table. Stores object ids as keys,
* and weak_entry_t structs as their values.
*/
struct weak_table_t {
weak_entry_t *weak_entries;
size_t num_entries;
uintptr_t mask;
uintptr_t max_hash_displacement;
};
/// Adds an (object, weak pointer) pair to the weak table.
id weak_register_no_lock(weak_table_t *weak_table, id referent,
id *referrer, bool crashIfDeallocating);
/// Removes an (object, weak pointer) pair from the weak table.
void weak_unregister_no_lock(weak_table_t *weak_table, id referent, id *referrer);
#if DEBUG
/// Returns true if an object is weakly referenced somewhere.
bool weak_is_registered_no_lock(weak_table_t *weak_table, id referent);
#endif
/// Called on object destruction. Sets all remaining weak pointers to nil.
void weak_clear_no_lock(weak_table_t *weak_table, id referent);
__END_DECLS
#endif /* _OBJC_WEAK_H_ */
下面依次开始分析 对象的weak
引用 和 释放。
二、weak
原理分析
调试所用代码:
- (void)my_weak {
__weak typeof(self)weakSelf = self;
self.block = ^ {
__strong typeof(weakSelf)strongSelf = weakSelf;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",strongSelf.name);
});
};
}
1、weak
引用 - 源码分析
clang
编译,报错如下:
cannot create __weak
reference because the current deployment target does not support weak references
__attribute__((objc_ownership(weak))) typeof(self)weakSelf = self
编译时不能创建__weak
引用,不支持;__weak
是运行时runtime
初始化并维护的。
运行工程,通过汇编断点调试可知源码在libobjc.A.dylib
.
libobjc.A.dylib`objc_initWeak:
1.1、初始化 - 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. // __weak指针的地址
* @param newObj Object ptr. // 对象ptr
*/
id
objc_initWeak(id *location, id newObj)
{
if (!newObj) {// 没有 objc
*location = nil;
return nil;
}
//
return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
(location, (objc_object*)newObj);
}
DontHaveOld
/ DoHaveNew
/ DoCrashIfDeallocating
:
// Template parameters.
enum HaveOld { DontHaveOld = false, DoHaveOld = true };
enum HaveNew { DontHaveNew = false, DoHaveNew = true };
1.2、更新弱变量 - storeWeak()
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.
/**
当CrashIfDeallocating 为 true 时,如果newObj正在进行deallocating或 newObj的类不支持弱引用,进程将被停止;
当CrashIfDeallocating 为 false 时,则存储 nil.
*/
enum CrashIfDeallocating {
DontCrashIfDeallocating = false, DoCrashIfDeallocating = true
};
template <HaveOld haveOld, HaveNew haveNew,
CrashIfDeallocating crashIfDeallocating>
static id
storeWeak(id *location, objc_object *newObj)
{
// initWeak 时:haveOld = NO, haveNew = YES, crashIfDeallocating = YES
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:
// 如果 weak指针之前弱引用过一个 obj,则将这个 obj 所对应的 SideTable 取出,赋值给 oldTable
if (haveOld) {
oldObj = *location;// 将旧值(__weak指针的值) 赋给 oldObj
oldTable = &SideTables()[oldObj];// SideTablesMap.get();
} else {
oldTable = nil;
}
if (haveNew) {
// weak指针要弱引用一个 obj,将这个 obj 所对应的 SideTable 取出,赋值给 onewTable
newTable = &SideTables()[newObj];
} else {
newTable = nil;
}
// lockTwo 加锁,保证多线程安全
SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);
// 有无旧值 && location与oldObj 是否一致
// 如果 有旧值 但 location与oldObj 不同,说明当前的location已经处理过oldObj,可是又被其他线程给修改了
if (haveOld && *location != oldObj) {
// 解锁, retry 重新处理 old
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
goto retry;
}
// 通过确保弱引用的对象没有未初始化的isa,防止 弱引用 和 初始化 之间死锁
// 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())
{ // cls != nil 且 cls 未初始化
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
class_initialize(cls, (id)newObj);// 去初始化 cls
// 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.
// cls 赋值给 previouslyInitializedClass 重新 retry
previouslyInitializedClass = cls;
goto retry;
}
}
// Clean up old value, if any.
// 如果weak指针之前弱引用过别的对象oldObj,则调用weak_unregister_no_lock,在oldObj的weak_entry_t中移除该weak指针地址
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方法,将weak指针的地址记录到newObj对应的weak_entry_t中
// weak_entry_t 插入到 全局 weak_table 哈希表中
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.
// 更新newObj的isa指针的weakly_referenced bit标志位
if (newObj && !newObj->isTaggedPointer()) {
newObj->setWeaklyReferenced_nolock();
}
// Do not set *location anywhere else. That would introduce a race.
// weak指针直接指向了newObj,且没有将newObj的引用计数+1
*location = (id)newObj;
}
else {
// No new value. The storage is not changed.
}
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
return (id)newObj;
}
第一次调用,所以是一个新的对象,也就是haveNew
的情况,获取到的是新的散列表SideTable
,通过weak_register_no_lock()
方法来进行插入。接着来分析weak_register_no_lock
函数,是怎么注册弱引用的.
1.3、注册一个新对(对象,弱指针
) - weak_register_no_lock()
weak_register_no_lock()
-->
注册一个新的(对象,弱指针)对
。如果弱对象entry
不存在,则新建一个entry
:
/**
* 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. // weak指针地址
*/
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;
//如果被弱引用的对象referent为nil 或者 被弱引用对象采用了 TaggedPointer 计数方式,直接返回
if (!referent || referent->isTaggedPointer()) return referent_id;
// ensure that the referenced object is viable
// 确认引用对象是可用的 - 没有被析构且支持weak引用
bool deallocating;
if (!referent->ISA()->hasCustomRR()) {// isa 左移获取是否析构的值 has_cxx_dtor
deallocating = referent->rootIsDeallocating();
/**
inline bool
objc_object::rootIsDeallocating()
{
if (isTaggedPointer()) return false;
if (isa.nonpointer) return isa.deallocating;// isa的deallocating
return sidetable_isDeallocating();
}
*/
}
else {// has_cxx_dtor = YES
BOOL (*allowsWeakReference)(objc_object *, SEL) =
(BOOL(*)(objc_object *, SEL))
object_getMethodImplementation((id)referent,
@selector(allowsWeakReference));
if ((IMP)allowsWeakReference == _objc_msgForward) {
return nil;
}
deallocating =
! (*allowsWeakReference)(referent, @selector(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
return nil;
}
}
// now remember it and where it is being stored
// 记录存储
// 在 weak_table 中找到被弱引用的对象referent所对应的 weak_entry,并将 referrer 加入到 weak_entry 中
weak_entry_t *entry;
if ((entry = weak_entry_for_referent(weak_table, referent))) {
// 已有 weak_entry 则将referent追加到weak_entry
append_referrer(entry, referrer);
}
else {
// 没有,则新建 weak_entry,并将其插入weak_table
weak_entry_t new_entry(referent, referrer);
weak_grow_maybe(weak_table);// 存储空间是否足够,若已超出3/4,则新开辟一个扩容到2倍的空间,并把旧空间释放free
weak_entry_insert(weak_table, &new_entry);// 插入new_entry
}
// Do not set *referrer. objc_storeWeak() requires that the
// value not change.
return referent_id;
}
1.3.1、插入新对象 weak_entry_insert()
:
/**
* Add new_entry to the object's table of weak references.
* Does not check whether the referent is already in the table.
*/
static void weak_entry_insert(weak_table_t *weak_table, weak_entry_t *new_entry)
{
weak_entry_t *weak_entries = weak_table->weak_entries;
ASSERT(weak_entries != nil);
// 插入位置 index
size_t begin = hash_pointer(new_entry->referent) & (weak_table->mask);// 哈希算法根据 弱引用对象 计算出开始插入的位置 index
size_t index = begin;
size_t hash_displacement = 0;
while (weak_entries[index].referent != nil) {
index = (index+1) & weak_table->mask;
if (index == begin) bad_weak_table(weak_entries);// 哈希冲突
hash_displacement++;
}
// while 直至 referent=nil,index 则是可插入位置
weak_entries[index] = *new_entry;
weak_table->num_entries++;
if (hash_displacement > weak_table->max_hash_displacement) {
// 更新 max_hash_displacement 的值
weak_table->max_hash_displacement = hash_displacement;
}
}
1.3.2、拼接 append_referrer()
:
/**
* 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)
{
// 判断 weak_entry 是否可使用 静态数组 inline_referrers
if (! entry->out_of_line()) {// weak_entry的out_of_line_ness == 2
// Try to insert inline. 尝试将 new_referrer 插入数组
// WEAK_INLINE_COUNT = 4
// inline_referrers[4] : inline_referrers固定的4
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
if (entry->inline_referrers[i] == nil) {
entry->inline_referrers[i] = new_referrer;
return;
}
}
// 静态数组位置满了不能插入,则开始使用 referrers动态数组
// Couldn't insert inline. Allocate out of line.
// 开辟out of line空间,空间大小: 4 * weak_referrer_t
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.
// 这个构造的表无效,但是grow_refs_and_insert将修复它并将他重新哈希。
// for 循环,将静态组的数据赋到新建的动态组中
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;// num_refs 赋值 4
entry->out_of_line_ness = REFERRERS_OUT_OF_LINE;// 赋值 2
entry->mask = WEAK_INLINE_COUNT-1;// mask = 3
entry->max_hash_displacement = 0;// 最大哈希量初始化为0
}
ASSERT(entry->out_of_line());
// 如果动态数组中元素个数 ≥ 数组总空间的3/4,则将数组空间扩容1倍,然后再将 new_referrer 插入数组
if (entry->num_refs >= TABLE_SIZE(entry) * 3/4) {
return grow_refs_and_insert(entry, new_referrer);
}
// 动态数组空间足够,开始计算 new_referrer 的 index,将 new_referrer 插入 weak_entry 中
// 计算 new_referrer 的插入位置 index
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;// 最大哈希量 更新
}
// 将 new_referrer 插入 referrers 的 index 位置
weak_referrer_t &ref = entry->referrers[index];
ref = new_referrer;
entry->num_refs++;// 已存数量++
}
* 弱引用对象的存储
- 总结:
针对被弱引用的对象,首先去全局弱引用表weak_table
的weak_entries[] 哈希数组
中寻找对应的weak_entry_t
,
- 如果
weak_entry_t
不存在,则会新建一个,并进行空间是否够用处理(已超出3/4则新开辟个2倍的空间并释放旧空间
),然后将新建的weak_entry_t
插入weak_table
的weak_entries数组
中对应的index
(由哈希函数算出)位置; - 如果
weak_entry_t
存在,将指向 被弱引用的对象地址的指针 referrer 通过函数append_referrer()
,插入到对应的weak_entry_t
中referrers
数组的index
位置,具体处理见上面源码分析.
2、weak
释放 - 源码分析
weak
销毁的过程是调用析构函数dealloc
,下面就来分析析构函数的原理。
- (void)dealloc {
_objc_rootDealloc(self);
}
void
_objc_rootDealloc(id obj)
{
ASSERT(obj);// obj 是否为空
obj->rootDealloc();
}
objc_object::rootDealloc()
inline void
objc_object::rootDealloc()
{
// 是 TaggedPointer 类型,直接返回
if (isTaggedPointer()) return; // fixme necessary?
if (fastpath(isa.nonpointer && // nonpointer 开启了优化的 isa 计数方式
!isa.weakly_referenced && // 对象有无被弱引用
!isa.has_assoc && // 对象有没有关联对象
!isa.has_cxx_dtor && // 对象有无自定义的C++ 析构函数
!isa.has_sidetable_rc)) // 对象有无用到 sideTable 做引用计数
{
assert(!sidetable_present());
// 都没有,直接 free
free(this);
}
else {
object_dispose((id)this);
}
}
不能直接释放,继续 --> object_dispose()
:
/***********************************************************************
* object_dispose
* fixme
* Locking: none
**********************************************************************/
id
object_dispose(id obj)
{
if (!obj) return nil;
objc_destructInstance(obj);
free(obj);
return nil;
}
2.1、首先销毁 C++析构函数
和关联对象
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.
// 如果有C++的析构函数,则将C++析构函数从类中销毁
if (cxx) object_cxxDestruct(obj);
// 有关联对象,将关联对象全部移除,并将自己也从 assocationManager的map中移除
if (assoc) _object_remove_assocations(obj);
// 继续 dealloc 处理对象的其他引用
obj->clearDeallocating();
}
return obj;
}
2.2、销毁其他
objc_object::clearDeallocating()
:
inline void
objc_object::clearDeallocating()
{
// 1. 要释放的对象没有采用优化过的isa引用计数
if (slowpath(!isa.nonpointer)) {
// Slow path for raw pointer isa.
sidetable_clearDeallocating();
}
// 2. 要释放的对象采用优化过的isa引用计数 且 有弱引用 或 有用sidetable来引用计数
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());
}
2.2.1、对象没有采用优化过的isa引用计数
sidetable_clearDeallocating()
:
void
objc_object::sidetable_clearDeallocating()
{
// 获取当前对象的 SideTable
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,拿到要释放的对象的引用计数
RefcountMap::iterator it = table.refcnts.find(this);
if (it != table.refcnts.end()) {
if (it->second & SIDE_TABLE_WEAKLY_REFERENCED) {
// 从 weak_table 中,找到要释放对象的 entry,对entry中referrers遍历将数组中的弱引用 全部置为 nil
weak_clear_no_lock(&table.weak_table, (id)this);
}
// 从引用计数表中 擦除当前对象的引用计数
table.refcnts.erase(it);
}
table.unlock();
}
2.2.2、对象采用了优化过的isa引用计数
objc_object::clearDeallocating_slow()
:
// Slow path of clearDeallocating()
// for objects with nonpointer isa
// that were ever weakly referenced
// or whose retain count ever overflowed to the side table.
NEVER_INLINE void
objc_object::clearDeallocating_slow()
{
ASSERT(isa.nonpointer && (isa.weakly_referenced || isa.has_sidetable_rc));
// 获取当前对象散列表 sideTable
SideTable& table = SideTables()[this];
table.lock();
if (isa.weakly_referenced) {
// 有弱引用,将全部弱引用置为 nil
weak_clear_no_lock(&table.weak_table, (id)this);
}
if (isa.has_sidetable_rc) {
// 使用了sideTable的辅助引用计数,直接在SideTable中擦除该对象的引用计数
table.refcnts.erase(this);
}
table.unlock();
}
弱引用了对象的 weak指针
置为 nil
weak_clear_no_lock(&table.weak_table, (id)this)
:
/**
* 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_table 中获取 entry
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;
// 弱引用该对象的 所有weak指针地址 的数组 referrers
// referrers 数组的 count
if (entry->out_of_line()) {
referrers = entry->referrers;
count = TABLE_SIZE(entry);
}
else {
referrers = entry->inline_referrers;
count = WEAK_INLINE_COUNT;
}
// 遍历,将referrers数组内的 weak指针 全部置为 nil
for (size_t i = 0; i < count; ++i) {
objc_object **referrer = referrers[i];
if (referrer) {
// 如果weak指针 弱引用了对象 referent,则将weak指针设置为nil
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_table中移除对象的entry
weak_entry_remove(weak_table, entry);
}
网友评论