__strong 修饰符
__strong
修饰符是 id
类型和对象类型默认的所有权修饰符。__strong
修饰符表示对对象的强引用。当前对象被其他对象引用时,会执行 retain
操作,引用计数器+1。当 retainCount = 0
时,该对象才会被销毁。由于所有权默认类型为 __strong
修饰符,所以不需要写上 __strong
。
id __strong obj = [NSObject alloc]init];
循环引用
如果仅通过 __strong
修饰符不能解决引用计数式内存管理必然会发生的循环引用问题。循环引用容易发生内存泄露。所谓内存泄露就是应当废弃的对象在超出其生命周期后继续存在。
__weak 修饰符
怎么样才能避免循环引用呢?使用 __weak
修饰符可以避免循环引用。__weak
修饰符不持有对象,在超出其变量作用域时,对象即被释放。并且在对象被废弃时,弱引用自动失效且处于 nil
被赋值的状态。
__weak 修饰符的实现
/*编译器模拟代码*/
id obj1;
objc_initWeak(&obj1,obj);
objc_destoryWeak(&obj1);
objc_initWeak
在 objc 源码中,通过 objc_initWeak
函数初始化附有 __weak
修饰符的变量,源码如下:
* __weak id weakPtr;
* (The non-nil case)
*
*
* 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
可以看到 objc_initWeak
函数的注释表明传入参数实际上是 ptr
指针 和 o
对象 调用 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(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指针之前指向了一个弱引用,则会调用weak_unregister_no_lock方法将旧的weak指针地址移除
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;
}
在 storeWeak
函数中的流程如下:
- 首先获取新旧引用散列表
oldTable
,newTable
,并加锁 - 如果指针当前有指向旧对象,则调用
weak_unregister_no_lock
函数 - 如果指针要指向的新对象存在,则调用
weak_register_no_lock
函数,并对新对象设置相关弱引用标志位setWeaklyReferenced_nolock
weak_unregister_no_lock
查看 weak_unregister_no_lock
函数源码如下:
/**
* Unregister an already-registered weak reference.
* This is used when referrer's storage is about to go away, but referent
* isn't dead yet. (Otherwise, zeroing referrer later would be a
* bad memory access.)
* Does nothing if referent/referrer is not a currently active weak reference.
* Does not zero referrer.
*
* FIXME currently requires old referent value to be passed in (lame)
* FIXME unregistration should be automatic if referrer is collected
*
* @param weak_table The global weak table.
* @param referent The object.
* @param referrer The weak reference.
*/
void
weak_unregister_no_lock(weak_table_t *weak_table, id referent_id,
id *referrer_id)
{
objc_object *referent = (objc_object *)referent_id;
objc_object **referrer = (objc_object **)referrer_id;
weak_entry_t *entry;
if (!referent) return;
if ((entry = weak_entry_for_referent(weak_table, referent))) {
remove_referrer(entry, referrer);
bool empty = true;
if (entry->out_of_line() && entry->num_refs != 0) {
empty = false;
}
else {
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
if (entry->inline_referrers[i]) {
empty = false;
break;
}
}
}
if (empty) {
weak_entry_remove(weak_table, entry);
}
}
// Do not set *referrer = nil. objc_storeWeak() requires that the
// value not change.
}
通过上面源码得知 weak_unregister_no_lock
的流程如下:
-
根据传入对象
referent_id
作为key
,通过weak_entry_for_referent
在传入的弱引用表(weak_table)中寻找对应weak_entry_t
类型的value
,赋值给entry
,entry
中保存的是一个对象的弱引用信息; -
找到
entry
后,在entry
中的inline_referrers
和referrers
遍历查找referrer
并置 nil,referrers
中存放的都是弱引用变量的地址; -
遍历
entry
的inline_referrers
中是否还有引用,如果无引用则移除entry
因此,weak_unregister_no_lock
其实就是删除对应的弱引用地址。
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,
@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;
}
}
// 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);
weak_grow_maybe(weak_table);
weak_entry_insert(weak_table, &new_entry);
}
// Do not set *referrer. objc_storeWeak() requires that the
// value not change.
return referent_id;
}
通过上面源码得知 weak_register_no_lock
的流程如下:
- 判断对象是否存在或者是一个
Tagged Pointer
,直接返回对象 - 根据
deallocating
(对象是否正在释放的标志和对象是否支持弱引用)和入参crashIfDeallocating
判断是否中止程序运行。 - 在
weak_table_t
中去找referent
对应的weak_entry_t
,如果能找到entry
,则调用append_referrer
函数把对象弱引用的指针referrer
插入weak_entry_t
的哈希数组中(或者是定长为 4 的内部数组中)。 - 如果没有找到对应的
weak_entry_t
,则首先创建一个new_entry
,然后先执行weak_grow_maybe
扩容,然后调用weak_entry_insert
把new_entry
插入weak_table_t
的哈希数组中。
因此,weak_register_no_lock
其实就是保存对应的弱引用地址。
objc_destoryWeak
在离开作用域后,__weak
修饰的对象会调用 objc_destoryWeak
函数,源码如下:
void
objc_destroyWeak(id *location)
{
(void)storeWeak<DoHaveOld, DontHaveNew, DontCrashIfDeallocating>
(location, nil);
}
可以看到 objc_destroyWeak
函数依旧调用 storeWeak
函数,传递的 newObj
参数为 nil
,用于清除相应的弱引用指针地址。
__uunsafe_unretained 修饰符
__unsafe_unretained
修饰符正如其名 unsafe
所示,是不安全的修饰符。附有 __unsafe_unretained
修饰符的变量不属于编译器的内存管理对象。
_autoreleasing 修饰符
__autoreleasing
修饰符 等价于 autorelease
方法。
网友评论