概述
object默认的修饰符是__strong,然而在开发中我们也经常使用__weak,用它来解决循环引用的问题:两个对象相互引用无法释放,造成内存泄露。用__weak来破坏一个强引用,来达到正常释放的目的。这种情况常见于block中,但是有没有想过为什么block会强引用block中的对象呢?答案也很简单,我们手动或者编译器帮我们对block进行了copy的操作,所以block会对其块中的对象就行强引用
!这个是一个小知识点,有的面试中可能会问到。
上面的都不是本篇文章的重点,重点是用__weak修饰的变量有两个特点:1.__weak修饰的对象释放后会自动致为nil;2.__weak修饰的对象注册到autoreleasepool中。今天我们来重点分析下__weak的这两个特点。
通过苹果开源代码,找到了与__weak相关变量函数:
第一点:__weak修饰的对象释放后会自动致为ni
//创建
id objc_initWeak(id *location, id newObj)
{
if (!newObj) {
*location = nil;
return nil;
}
return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
(location, (objc_object*)newObj);
}
//销毁
void objc_destroyWeak(id *location)
{
(void)storeWeak<DoHaveOld, DontHaveNew, DontCrashIfDeallocating>
(location, nil);
}
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;
}
__weak修饰的变量由weak表统一管理,这个和引用计数表一样,都是作为散列表被实现的。
例如:
__weak id weakObj = obj;
转换=>
objc_initWeak(&weakObj, obj);
objc_destroyWeak(&weakObj);
objc_initWeak
函数把obj地址作为键值,把weakObj的地址注册到weak表中。当销毁的时候objc_destroyWeak
调用storeWeak
传入nil.对于一个键值可以注册多个变量的地址,以obj地址作为键值的weak变量可能是多个,就会以ojb为键值进行检索,并做了以下操作:
(1)从weak表中获取废弃对象的地址为键值的记录
(2)将记录中的__weak修饰符修饰的变量的地址赋值为nil
(3)从weak表中删除该记录
(4)从引用计数表中删除废弃对象的地址为键值的记录
所以从上面的过程来看,__weak修饰的对象废弃之后,就会至为nil。由此也能看出来,这些步骤的执行必定会消耗一定的CPU资源,最好的办法是在需要使用__weak的时候才去使用!!!
第二点:__weak修饰的对象注册到autoreleasepool中
__weak id weakObj = obj;
NSLog(@"%@", weakObj);
转换=>
objc_initWeak(&weakObj, obj);
let tmp = objc_loadWeakRetained(&weakObj);
objc_autorelease(tmp);
NSLog(@"%@", tmp);
objc_destroyWeak(&weakObj);
当有__weak修饰符的情况下多了objc_loadWeakRetained函数和objc_autorelease函数的调用。
objc_loadWeakRetained:取出__weak变量并retain
objc_autorelease:将对象注册到autoreleasepool中,因为对象为弱引用在访问的过程中随时都有可能释放,如果那它加入到autoreleasepool中,那么在autoreleasepool未释放之前对象可以放心使用了。
另外建议在使用__weak的时候同时使用__strong来强引用weak指针。如下:
__weak typeof(self) weakSlef = self;
self.block = ^{
__strong typeof(weakSlef) strongSelf = weakSelf;
};
这样做的目的是为了在block执行的过程中self被释放,导致一些异常问题甚至是crash。
奇巧淫技
另外介绍一种解决block循环引用的方法:
__weak typeof(self) weakSlef = self;
self.block = ^{
self.block = nil;
或
self = nil;
};
任何一个至为nil都可以解除循环引用,但是有个缺点:必须执行block才能解除循环引用,可在一些会造成循环引用的但又不能用__weak的时候使用。
参考:
《Objective-C高级编程》
最后
以上就是开心钢笔为你收集整理的__weak详解的全部内容,希望文章能够帮你解决__weak详解所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复