OC Runtime之Weak(3)---探究NSObject
2017-08-10 本文已影响0人
08a2d4bd26c9
Runtime对于weak_table_t还有一层封装,也就是SideTable。这层封装对于弱引用机制的主要目的是解决线程安全的问题,因为之前也提到weak_table_t的各种操作并不是线程安全的。
struct SideTable {
spinlock_t slock;
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);
};
RefcountMap用于OC的引用计数机制,此处不表。slock实际上是os_unfair_lock_s类型,用于处理线程安全的问题。SideTable提供的方法都与锁有关。
NSObject的实现中和弱引用相关的的最重要的函数就是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;
// 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;
}
首先请忽视C++奇怪的各种语法,比如为什么template中的枚举会跑到参数里去(手动哭泣)。haveOld和haveNew主要用来控制具体的操作,比如给一个对象新增一个弱引用,或者是删除一个弱引用。函数的前半部分主要在处理线程安全和类加载的问题,和弱引用的逻辑关系不大。我们从 if (haveOld) { 这一行看起,如果haveOld是true,则说明参数里的弱引用已经指向了一个对象,需要调用weak_unregister_no_lock方法解除关联。如果haveNew是true,则调用weak_register_no_lock关联目标对象和弱应用,并且将弱引用设置为指向目标对象。因此通过设置haveOld和haveNew不同的组合,可以控制该函数完成不同的操作。
纯逻辑来看,这一部分相对简单,但考虑到线程安全,类的初始化,Tagged Pointer等等细节,要处理的地方还是非常多。这一系列的文章主要介绍了弱引用机制的实现,帮助大家对弱引用有一个更详细更立体的理解,未涉及到太多的具体的底层技术细节,如果读者有兴趣的话,可以继续深入探究。后续的runtime相关的文章也可能会继续提及。