您好,欢迎访问三七文档
网址:edu.51CTO.comweak的生命周期weak的生命周期我们都知道weak表示的是一个弱引用,这个引用不会增加对象的引用计数,并且在所指向的对象被释放之后,weak指针会被设置的为nil。weak引用通常是用于处理循环引用的问题,如代理及block的使用中,相对会较多的使用到weak。之前对weak的实现略有了解,知道它的一个基本的生命周期,但具体是怎么实现的,了解得不是太清晰。今天又翻了翻《Objective-C高级编程》关于__weak的讲解,在此做个笔记。我们以下面这行代码为例:代码清单1:示例代码1.{2.id__weakobj1=obj;3.}当我们初始化一个weak变量时,runtime会调用objc_initWeak函数。这个函数在Clang中的声明如下:1.idobjc_initWeak(id*object,idvalue);其具体实现如下:1.idobjc_initWeak(id*object,idvalue)2.{3.*object=0;4.returnobjc_storeWeak(object,value);5.}示例代码轮换成编译器的模拟代码如下:1.idobj1;2.objc_initWeak(&obj1,obj);因此,这里所做的事是先将obj1初始化为0(nil),然后将obj1的地址及obj作为参数传递给objc_storeWeak函数。网址:edu.51CTO.comobjc_initWeak函数有一个前提条件:就是object必须是一个没有被注册为__weak对象的有效指针。而value则可以是null,或者指向一个有效的对象。如果value是一个空指针或者其指向的对象已经被释放了,则object是zero-initialized的。否则,object将被注册为一个指向value的__weak对象。而这事应该是objc_storeWeak函数干的。objc_storeWeak的函数声明如下:1.idobjc_storeWeak(id*location,idvalue);其具体实现如下:1.idobjc_storeWeak(id*location,idnewObj)2.{3.idoldObj;4.SideTable*oldTable;5.SideTable*newTable;6.7.......8.9.//Acquirelocksforoldandnewvalues.10.//Orderbylockaddresstopreventlockorderingproblems.11.//Retryiftheoldvaluechangesunderneathus.12.retry:13.oldObj=*location;14.15.oldTable=SideTable::tableForPointer(oldObj);16.newTable=SideTable::tableForPointer(newObj);17.18.......19.20.if(*location!=oldObj){21.OSSpinLockUnlock(lock1);22.#ifSIDE_TABLE_STRIPE123.if(lock1!=lock2)OSSpinLockUnlock(lock2);24.#endif25.gotoretry;26.}27.28.if(oldObj){29.weak_unregister_no_lock(&oldTable-weak_table,oldObj,location);30.}31.if(newObj){网址:edu.51CTO.com32.newObj=weak_register_no_lock(&newTable-weak_table,newObj,location);33.//weak_register_no_lockreturnsNULLifweakstoreshouldberejected34.}35.//Donotset*locationanywhereelse.Thatwouldintroducearace.36.*location=newObj;37.38.......39.40.returnnewObj;41.}我们撇开源码中各种锁操作,来看看这段代码都做了些什么。在此之前,我们先来了解下weak表和SideTable。weak表是一个弱引用表,实现为一个weak_table_t结构体,存储了某个对象相关的的所有的弱引用信息。其定义如下(具体定义在objc-weak.h中):1.structweak_table_t{2.weak_entry_t*weak_entries;3.size_tnum_entries;4.......5.};其中weak_entry_t是存储在弱引用表中的一个内部结构体,它负责维护和存储指向一个对象的所有弱引用hash表。其定义如下:1.structweak_entry_t{2.DisguisedPtrobjc_objectreferent;3.union{4.struct{5.weak_referrer_t*referrers;6.uintptr_tout_of_line:1;7.......8.};9.struct{10.//out_of_line=0isLSBofoneofthese(don'tcarewhich)11.weak_referrer_tinline_referrers[WEAK_INLINE_COUNT];12.};13.};14.};网址:edu.51CTO.com其中referent是被引用的对象,即示例代码中的obj对象。下面的union即存储了所有指向该对象的弱引用。由注释可以看到,当out_of_line等于0时,hash表被一个数组所代替。另外,所有的弱引用对象的地址都是存储在weak_referrer_t指针的地址中。其定义如下:typedefobjc_object**weak_referrer_t;SideTable是一个用C++实现的类,它的具体定义在NSObject.mm中,我们来看看它的一些成员变量的定义:1.classSideTable{2.private:3.staticuint8_ttable_buf[SIDE_TABLE_STRIPE*SIDE_TABLE_SIZE];4.5.public:6.7.RefcountMaprefcnts;8.weak_table_tweak_table;9.10.......11.12.}RefcountMaprefcnts,大家应该能猜到这个做什么用的吧?看着像是引用计数什么的。哈哈,貌似就是啊,这东东存储了一个对象的引用计数的信息。当然,我们在这里不去探究它,我们关注的是weak_table。这个成员变量指向的就是一个对象的weak表。了解了weak表和SideTable,让我们再回过头来看看objc_storeWeak。首先是根据weak指针找到其指向的老的对象:1.oldObj=*location;然后获取到与新旧对象相关的SideTable对象:1.oldTable=SideTable::tableForPointer(oldObj);2.newTable=SideTable::tableForPointer(newObj);3.4.下面要做的就是在老对象的weak表中移除指向信息,而在新对象的weak表中建立关联信息:5.6.if(oldObj){7.weak_unregister_no_lock(&oldTable-weak_table,oldObj,location);8.}网址:edu.51CTO.com9.if(newObj){10.newObj=weak_register_no_lock(&newTable-weak_table,newObj,location);11.//weak_register_no_lockreturnsNULLifweakstoreshouldberejected12.}接下来让弱引用指针指向新的对象:1.*location=newObj;最后会返回这个新对象:1.returnnewObj;objc_storeWeak的基本实现就是这样。当然,在objc_initWeak中调用objc_storeWeak时,老对象是空的,所有不会执行weak_unregister_no_lock操作。而当weak引用指向的对象被释放时,又是如何去处理weak指针的呢?当释放对象时,其基本流程如下:调用objc_release因为对象的引用计数为0,所以执行dealloc在dealloc中,调用了_objc_rootDealloc函数在_objc_rootDealloc中,调用了object_dispose函数调用objc_destructInstance最后调用objc_clear_deallocating我们重点关注一下最后一步,objc_clear_deallocating的具体实现如下:1.voidobjc_clear_deallocating(idobj)2.{3.......4.5.SideTable*table=SideTable::tableForPointer(obj);6.7.//clearanyweaktableitems8.//clearextraretaincountanddeallocatingbit9.//(fixmewarnorabortifextraretaincount==0?)10.OSSpinLockLock(&table-slock);网址:edu.51CTO.com11.if(seen_weak_refs){12.arr_clear_deallocating(&table-weak_table,obj);13.}14.......15.}我们可以看到,在这个函数中,首先取出对象对应的SideTable实例,如果这个对象有关联的弱引用,则调用arr_clear_deallocating来清除对象的弱引用信息。我们来看看arr_clear_deallocating具体实现:1.PRIVATE_EXTERNvoidarr_clear_deallocating(weak_table_t*weak_table,idreferent){2.{3.weak_entry_t*entry=weak_entry_for_referent(weak_table,referent);4.if(entry==NULL){5.......6.return;7.}8.//zerooutreferences9.for(inti=0;ientry-referrers.num_allocated;++i){10.id*referrer=entry-referrers.refs[i].referrer;11.if(referrer){12.if(*referrer==referent){13.*referrer=nil;14.}15.elseif(*referrer){16._objc_inform(__weakvariable@%pholds%pinsteadof%p\n,referrer,*referrer,referent);17.}18.}19.}20.21.weak_entry_remove_no_lock(weak_table,entry);22.weak_table-num_weak_refs--;23.}24.}这个函数首先是找出对象对应的weak_entry_t链表,然后挨个将弱引用置为nil。最后清理对象的记录。通过上面的描述,我们基本能了解一个weak引用从生到死的过程。从这个流程可以看出,一个weak引用的处理涉及各种查表、添加与删除操作,还是有一定消耗的。所以如果网址:edu.51CTO.com大量使用__weak变量的话,会对性能造成一定的影响
本文标题:weak的生命周期
链接地址:https://www.777doc.com/doc-7747440 .html