www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當(dāng)前位置:首頁(yè) > > 充電吧
[導(dǎo)讀]摘要:對(duì)許多開發(fā)者而言,ARC最令人失望之處莫過(guò)于蘋果公司讓ARC來(lái)管理內(nèi)存。不幸的是ARC沒(méi)有循環(huán)引用檢測(cè)器,因此很容易出現(xiàn)Retain Cycle現(xiàn)象,從而迫使開發(fā)者在編碼時(shí)要采取特殊的預(yù)防措施。

摘要:對(duì)許多開發(fā)者而言,ARC最令人失望之處莫過(guò)于蘋果公司讓ARC來(lái)管理內(nèi)存。不幸的是ARC沒(méi)有循環(huán)引用檢測(cè)器,因此很容易出現(xiàn)Retain Cycle現(xiàn)象,從而迫使開發(fā)者在編碼時(shí)要采取特殊的預(yù)防措施。

ARC中的Retain Cycle就像日本B級(jí)恐怖電影一樣。開始使用Cocoa或Cocoa Touch做開發(fā)時(shí),你甚至不會(huì)在意它的存在。直到有一天應(yīng)用程序由于內(nèi)存泄漏而出現(xiàn)了崩潰現(xiàn)象,你才意識(shí)到它們的存在,看到像幽靈一樣的Retain Cycle無(wú)處不在。隨著歲月流逝,你學(xué)會(huì)適應(yīng)它們,發(fā)現(xiàn)它們,避免它們……但最終恐慌還在,無(wú)孔不入。

包括我在內(nèi),對(duì)于許多開發(fā)人員來(lái)說(shuō),ARC的最令人失望之處莫過(guò)于蘋果公司讓ARC來(lái)管理內(nèi)存。不幸的是ARC沒(méi)有循環(huán)引用檢測(cè)器,因此很容易出現(xiàn)Retain Cycle現(xiàn)象,從而迫使開發(fā)人員在編碼時(shí)要采取特殊的預(yù)防措施。

對(duì)于iOS開發(fā)人員來(lái)說(shuō),Retain Cycle是個(gè)難點(diǎn)。在網(wǎng)上有很多誤導(dǎo)信息[1][2],人們所給出的這些錯(cuò)誤信息和修復(fù)方法甚至?xí)?dǎo)致應(yīng)用出現(xiàn)新的問(wèn)題,甚至崩潰掉,基于這樣的情況,本文中我會(huì)闡明主題,給讀者一些啟發(fā)。

相關(guān)理論一瞥

Cocoa框架內(nèi)存管理可以追溯到MRR(Manual Retain Release),在MRR中,開發(fā)人員在創(chuàng)建對(duì)象的時(shí)候,要為每個(gè)內(nèi)存中的對(duì)象聲明所有權(quán)。并且,當(dāng)不再需要該對(duì)象時(shí),要放棄所有權(quán)。MRR通過(guò)引用計(jì)數(shù)系統(tǒng)來(lái)實(shí)現(xiàn)這種所有權(quán)機(jī)制。每個(gè)對(duì)象都被分配一個(gè)計(jì)數(shù)器指示被“擁有”了多少次,每次加一,釋放對(duì)象的時(shí)候每次減一。當(dāng)引用計(jì)數(shù)變成零的時(shí)候,該對(duì)象將不復(fù)存在。對(duì)于開發(fā)人員來(lái)說(shuō),不得不手動(dòng)維護(hù)引用計(jì)數(shù)真的是很煩人的事情,于是蘋果公司引入了自動(dòng)引用計(jì)數(shù)(Automated Reference Counting, ARC)機(jī)制,免得開發(fā)人員手動(dòng)添加保留(retain)和釋放(release)指令,讓他們專注于解決應(yīng)用程序的問(wèn)題。在ARC環(huán)境下,開發(fā)人員要將一個(gè)變量定義為“strong”或“weak”。使用weak的話應(yīng)用程序中被聲明的對(duì)象不會(huì)被retain,而使用strong聲明的對(duì)象將會(huì)被retain,并且其引用計(jì)數(shù)加一。


為什么要在乎?

ARC的問(wèn)題在于容易導(dǎo)致Retain Cycle,它發(fā)生在兩個(gè)不同的對(duì)象間彼此包含強(qiáng)引用的時(shí)候。試想一個(gè)Book對(duì)象包含一系列的Page對(duì)象,每個(gè)Page對(duì)象有個(gè)屬性指向該頁(yè)所在的這本書。當(dāng)你釋放掉指向Book和Page的變量時(shí),Book和Page之間還存在著強(qiáng)引用。因此,即使沒(méi)有變量指向Book和Page了,Book和Page及其所占用的內(nèi)存也不會(huì)被釋放掉。

不幸之處在于并非所有Retain Cycle都很容易被發(fā)現(xiàn)。對(duì)象之間的傳遞關(guān)系(A引用B,B轉(zhuǎn)而引用C,C引用A)會(huì)導(dǎo)致Retain Cycle。更糟糕的是Objective-C里的塊(block)和Swift里的閉包(closure)都被認(rèn)為是獨(dú)立的內(nèi)存對(duì)象。因此,任何在塊或閉包內(nèi)對(duì)像的引用都將會(huì)對(duì)其變量做retain操作。因此,如果對(duì)象仍然retain這個(gè)塊的話,就會(huì)導(dǎo)致潛在的Retain Cycle發(fā)生。

Retain Cycle可以成為應(yīng)用程序潛在的危害,導(dǎo)致內(nèi)存消耗過(guò)高,性能低下和崩潰。但還沒(méi)有來(lái)自于蘋果公司的文檔,針對(duì)Retain Cycle可能發(fā)生的不同場(chǎng)景,以及如何避免進(jìn)行描述。這導(dǎo)致了一些誤解并形成了不良的編程習(xí)慣。

用例場(chǎng)景

那么閑話少說(shuō),我們一起來(lái)分析一些場(chǎng)景,確定它們是否會(huì)導(dǎo)致Retain Cycle以及如何避免:

父子對(duì)象關(guān)系

這是Retain Cycle的典型例子。不幸的是這也是蘋果公司唯一給出相關(guān)解決方案文檔的例子。就是我上面描述的Book和Page對(duì)象的例子。這種情況的典型解決方案是把Child類里面的代表父類的變量定義成weak,這樣就可以避免Retain Cycle。


[cpp]?view plaincopyclass?Parent?{?? ???var?name:?String?? ???var?child:?Child??? ???init(name:?String)?{?? ??????self.name?=?name?? ???}?? }?? class?Child?{?? ???var?name:?String?? ???weak?var?parent:?Parent!?? ???init(name:?String,?parent:?Parent)?{?? ??????self.name?=?name?? ??????self.parent?=?parent?? ???}?? }??


在Swift語(yǔ)言中,代表父類的變量是個(gè)弱變量的事實(shí)迫使我們將其定義為可選類型。不使用可選類型的另一種做法是將父類型對(duì)象聲明為“unowned”(意味著我們不會(huì)對(duì)變量聲明進(jìn)行內(nèi)存管理或聲明所有權(quán))。然而在這種情況下,我們必須非常仔細(xì)地確保只有一個(gè)Child實(shí)例指向Parent,Parent就不能是nil,否則程序就會(huì)崩潰:


[cpp]?view plaincopyclass?Parent?{?? ???var?name:?String?? ???var?child:?Child??? ???init(name:?String)?{?? ??????self.name?=?name?? ???}?? }?? class?Child?{?? ???var?name:?String?? ???unowned?var?parent:?Parent?? ???init(name:?String,?parent:?Parent)?{?? ??????self.name?=?name?? ??????self.parent?=?parent?? ???}?? }?? var?parent:?Parent!?=?Parent(name:?"John")?? var?child:?Child!?=?Child(name:?"Alan",?parent:?parent)?? parent?=?nil?? child.parent?<==?possible?crash?here!??


一般來(lái)說(shuō)公認(rèn)的做法是父對(duì)象必須擁有(強(qiáng)引用)其子對(duì)象,這些子對(duì)象對(duì)其父對(duì)象應(yīng)該只保持一個(gè)弱引用。這同樣適用于集合,集合必須擁有其所包含的對(duì)象。

包含在實(shí)例變量中的塊和閉包

另一個(gè)經(jīng)典的例子雖然不是那么直觀,但正如我們之前所說(shuō)的那樣,閉包和塊是獨(dú)立的內(nèi)存對(duì)象,并retain了它們所引用的對(duì)象。因此如果我們有一個(gè)包含閉包變量的類,這個(gè)變量又恰好引用了其所擁有對(duì)象的屬性或方法,由于閉包通過(guò)創(chuàng)建一個(gè)強(qiáng)引用而“捕獲”了自己,就會(huì)有Retain Cycle發(fā)生。


[cpp]?view plaincopyclass?MyClass?{?? ???lazy?var?myClosureVar?=?{?? ??????self.doSomething()?? ???}?? }??


這種情況下的解決方法是將自身定義成“weak”版本,并且將此弱引用賦給閉包或塊。在Objective-C語(yǔ)言中,要定義個(gè)新的變量:


[cpp]?view plaincopy-?(id)?init()?{?? ???__weak?MyClass?*?weakSelf?=?self;?? ???self.myClosureVar?=?^{?? ??????[weakSelf?doSomething];?? ???}?? }??


而在Swift語(yǔ)言中,我們只需要指定“[weak self] in”作為閉包的啟動(dòng)參數(shù):


[cpp]?view plaincopyvar?myClosureVar?=?{?? ???[weak?self]?in?? ???self?.doSomething()?? }??


這樣一來(lái),當(dāng)閉包快執(zhí)行完畢時(shí),self變量不會(huì)被強(qiáng)制retain,因此會(huì)得到釋放,打破循環(huán)。注意,當(dāng)聲明為weak時(shí),self在閉包內(nèi)是如何變成可選類型的。

GCD中的dispatch_async

與一貫的認(rèn)識(shí)相反,dispatch_async本身并不會(huì)導(dǎo)致Retain Cycle。


[cpp]?view plaincopydispatch_async(queue,?{?()?->?Void?in?? ???self.doSomething();??? });??


在這里,閉包對(duì)self強(qiáng)引用,但是類(self)的實(shí)例對(duì)閉包沒(méi)有任何強(qiáng)引用,因此一旦閉包結(jié)束,它將被釋放,也不會(huì)有環(huán)出現(xiàn),然而,有的時(shí)候會(huì)被(錯(cuò)誤地)認(rèn)為這種情況會(huì)導(dǎo)致Retain Cycle。一些開發(fā)人員甚至一針見血地指出將塊或閉包內(nèi)所有對(duì)“self”的引用都聲明為weak:


[cpp]?view plaincopydispatch_async(queue,?{?? ???[weak?self]?in?? ???self?.doSomething()?? })??


在我看來(lái),對(duì)每種情況都這樣做并不是好的做法。我們假設(shè)某個(gè)對(duì)象啟動(dòng)了一個(gè)長(zhǎng)時(shí)間的后臺(tái)任務(wù)(比如從網(wǎng)絡(luò)上下載一些東西),然后調(diào)用了一個(gè)“self”方法。如果對(duì)self傳遞一個(gè)弱引用的話,那么類會(huì)在閉包結(jié)束之前完成它的生命周期。因此,當(dāng)調(diào)用 doSomething()的時(shí)候,類的實(shí)例已經(jīng)不存在了,所以這個(gè)方法永遠(yuǎn)不會(huì)被執(zhí)行。這種情況下,(蘋果公司)建議的解決方案是對(duì)閉包內(nèi)的弱引用(???)聲明一個(gè)強(qiáng)引用:


[cpp]?view plaincopydispatch_async(queue,?{?? ???[weak?self]?in?? ???if?let?strongSelf?=?self?{?? ??????strongSelf.doSomething()?? ???}?? })??


我不僅發(fā)現(xiàn)語(yǔ)法冗長(zhǎng)單調(diào),缺乏直觀性,甚至令人感到厭惡,而且使閉包作為獨(dú)立的處理實(shí)體的打算也落空了。我認(rèn)為要理解對(duì)象的生命周期,確切地明白什么時(shí)候應(yīng)該為實(shí)例聲明一個(gè)內(nèi)部的weak版,還要知道對(duì)象存續(xù)期間會(huì)有哪些影響。但話又說(shuō)回來(lái),這正是我解決應(yīng)用程序的問(wèn)題時(shí)分散我注意力的地方,如果Cocoa框架中沒(méi)有使用ARC的話,這些都是沒(méi)有必要寫的代碼。

局部的閉包和塊

沒(méi)有引用或包含任何實(shí)例或類變量的函數(shù)局部閉包和塊本身不會(huì)導(dǎo)致Retain Cycle。常見的例子就是UIView的animateWithDuration方法:


[cpp]?view plaincopyfunc?myMethod()?{?? ???...?? ???UIView.animateWithDuration(0.5,?animations:?{?()?->?Void?in?? ??????self.someOutlet.alpha?=?1.0?? ??????self.someMethod()?? ???})?? }??


對(duì)于dispatch_async和其他GCD相關(guān)的方法,我們不用擔(dān)心沒(méi)有被類實(shí)例強(qiáng)引用的局部閉包和塊,它們不會(huì)發(fā)生Retain Cycle。

代理方案

代理(delegation)是使用弱引用避免Retain Cycle的一個(gè)典型場(chǎng)景。將委托聲明為weak一直是一種不錯(cuò)的做法(并且還算安全)。在Objective-C中:


[cpp]?view plaincopy@property?(nonatomic,?weak)?id?

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

LED驅(qū)動(dòng)電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: 驅(qū)動(dòng)電源

在工業(yè)自動(dòng)化蓬勃發(fā)展的當(dāng)下,工業(yè)電機(jī)作為核心動(dòng)力設(shè)備,其驅(qū)動(dòng)電源的性能直接關(guān)系到整個(gè)系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動(dòng)勢(shì)抑制與過(guò)流保護(hù)是驅(qū)動(dòng)電源設(shè)計(jì)中至關(guān)重要的兩個(gè)環(huán)節(jié),集成化方案的設(shè)計(jì)成為提升電機(jī)驅(qū)動(dòng)性能的關(guān)鍵。

關(guān)鍵字: 工業(yè)電機(jī) 驅(qū)動(dòng)電源

LED 驅(qū)動(dòng)電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個(gè)照明設(shè)備的使用壽命。然而,在實(shí)際應(yīng)用中,LED 驅(qū)動(dòng)電源易損壞的問(wèn)題卻十分常見,不僅增加了維護(hù)成本,還影響了用戶體驗(yàn)。要解決這一問(wèn)題,需從設(shè)計(jì)、生...

關(guān)鍵字: 驅(qū)動(dòng)電源 照明系統(tǒng) 散熱

根據(jù)LED驅(qū)動(dòng)電源的公式,電感內(nèi)電流波動(dòng)大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關(guān)鍵字: LED 設(shè)計(jì) 驅(qū)動(dòng)電源

電動(dòng)汽車(EV)作為新能源汽車的重要代表,正逐漸成為全球汽車產(chǎn)業(yè)的重要發(fā)展方向。電動(dòng)汽車的核心技術(shù)之一是電機(jī)驅(qū)動(dòng)控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機(jī)驅(qū)動(dòng)系統(tǒng)中的關(guān)鍵元件,其性能直接影響到電動(dòng)汽車的動(dòng)力性能和...

關(guān)鍵字: 電動(dòng)汽車 新能源 驅(qū)動(dòng)電源

在現(xiàn)代城市建設(shè)中,街道及停車場(chǎng)照明作為基礎(chǔ)設(shè)施的重要組成部分,其質(zhì)量和效率直接關(guān)系到城市的公共安全、居民生活質(zhì)量和能源利用效率。隨著科技的進(jìn)步,高亮度白光發(fā)光二極管(LED)因其獨(dú)特的優(yōu)勢(shì)逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

關(guān)鍵字: 發(fā)光二極管 驅(qū)動(dòng)電源 LED

LED通用照明設(shè)計(jì)工程師會(huì)遇到許多挑戰(zhàn),如功率密度、功率因數(shù)校正(PFC)、空間受限和可靠性等。

關(guān)鍵字: LED 驅(qū)動(dòng)電源 功率因數(shù)校正

在LED照明技術(shù)日益普及的今天,LED驅(qū)動(dòng)電源的電磁干擾(EMI)問(wèn)題成為了一個(gè)不可忽視的挑戰(zhàn)。電磁干擾不僅會(huì)影響LED燈具的正常工作,還可能對(duì)周圍電子設(shè)備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來(lái)解決L...

關(guān)鍵字: LED照明技術(shù) 電磁干擾 驅(qū)動(dòng)電源

開關(guān)電源具有效率高的特性,而且開關(guān)電源的變壓器體積比串聯(lián)穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機(jī)重量也有所下降,所以,現(xiàn)在的LED驅(qū)動(dòng)電源

關(guān)鍵字: LED 驅(qū)動(dòng)電源 開關(guān)電源

LED驅(qū)動(dòng)電源是把電源供應(yīng)轉(zhuǎn)換為特定的電壓電流以驅(qū)動(dòng)LED發(fā)光的電壓轉(zhuǎn)換器,通常情況下:LED驅(qū)動(dòng)電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: LED 隧道燈 驅(qū)動(dòng)電源
關(guān)閉