轉(zhuǎn)載請注明出處:http://www.olinone.com/
今天终娃,跟大家聊聊“自釋放”思想在iOS開發(fā)中的應(yīng)用肿男,何為“自釋放”纲堵?可以簡單的理解為對象在生命周期結(jié)束后自動清理回收所有與其相關(guān)的資源或鏈接幸逆,這個清理不僅僅包括對象內(nèi)存的回收棍辕,還包括對象解耦以及附屬事件的清理等,比如定時器的自我停止还绘、KVO對象的監(jiān)聽移除等
對象內(nèi)存的回收
開發(fā)中楚昭,對象管理的基本原則——誰創(chuàng)建誰釋放。但是拍顷,非ARC工程中抚太,我們會用autorelease來標記一個對象,告訴編輯器昔案,這個對象我不負責(zé)釋放尿贫,此時,這個對象就變成了“自釋放”對象踏揣,當(dāng)其不再需要時庆亡,系統(tǒng)就會自動回收其內(nèi)存。而ARC工程中捞稿,所有對象對于我們來說都是自釋放對象又谋,很高興拼缝,我們不再需要處處留意內(nèi)存泄露的問題,可以把更多的精力放在業(yè)務(wù)邏輯上彰亥,但是這并不意味著真的沒有內(nèi)存泄露咧七,試試這個工具HJNSObjectRelease,也許你會有意想不到的收獲剩愧。
定時器的自釋放
定時器與一般對象不同猪叙,當(dāng)創(chuàng)建完定時器后,其并不會自我釋放仁卷,需要在適當(dāng)時刻invalidate穴翩。在實際開發(fā)中,也許你經(jīng)常會這樣創(chuàng)建定時器
1
self.timer=[NSTimerscheduledTimerWithTimeInterval:1.0target:selfselector:@selector(onTimerCount)userInfo:nilrepeats:YES];
然后在dealloc函數(shù)中將定時器invalidate锦积。很遺憾芒帕,你會發(fā)現(xiàn)程序永遠也不會執(zhí)行到dealloc函數(shù),因為NSTimer強引用target對象丰介,循環(huán)引用的出現(xiàn)必然導(dǎo)致內(nèi)存泄露背蟆。此時,你肯定非常想要一個weak target的定時器哮幢,很高興带膀,MSWeakTimer很好的滿足了你的需求。但是橙垢,Timer仍然沒有自我釋放垛叨,你仍然需要在dealloc中將其invalidate。那么柜某,如何才能不寫invalidate嗽元?定時器能否自釋放?我們先把這個問題放在一邊,接著往下看
KVO的自釋放
iOS開發(fā)中喂击,經(jīng)常會用到消息通知及KVO剂癌,也許你會這樣寫代碼
-(void)viewDidLoad{
[superviewDidLoad];
[[NSNotificationCenterdefaultCenter]addObserver:selfselector:@selector(onNotice)name:@"NoticeIdentifier"object:nil];
[selfaddObserver:targetforKeyPath:@"keyPath"options:NSKeyValueObservingOptionNewcontext:nil];
}
-(void)dealloc{
[[NSNotificationCenterdefaultCenter]removeObserver:selfname:@"NoticeIdentifier"object:nil];
[selfremoveObserver:targetforKeyPath:@"keyPath"];
}
隨著時間的積累,你會非常習(xí)慣這種寫法翰绊,并且蘋果也是這樣推薦的佩谷。但是慢慢你會發(fā)現(xiàn)所有對象的Dealloc函數(shù)都只做了這一件事,能不能不做這件事监嗜?FBKVOController也許會是一個不錯的選擇谐檀,Demo可以這樣寫
[self.KVOControllerobserve:clockkeyPath:@"date"options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNewblock:^(ClockView*clockView,Clock*clock,NSDictionary*change){
clockView.date=change[NSKeyValueChangeNewKey];
}];
FBKVOController的實現(xiàn)原理可以查看這篇文章,通過自釋放的實現(xiàn)秤茅,程序猿不再關(guān)心remove監(jiān)聽稚补。但是其還是有一定的局限性——對象無法監(jiān)聽自己的屬性,如果你的代碼是這樣的
[self.KVOControllerobserve:selfkeyPath:@"date"options:NSKeyValueObservingOptionNewblock:^(NSDictionary*change){
// to do
}];
很遺憾框喳,循環(huán)引用的問題又出現(xiàn)课幕,因為FBKVOController中的NSMapTable對象會retain key對象厦坛,具體代碼如下
[_objectInfosMapsetObject:infosforKey:object];
那么,FBKVOController是如何做到自釋放的乍惊?可以歸納為四個字——動態(tài)屬性杜秸。其為觀察者綁定動態(tài)屬性self.KVOController,動態(tài)綁定的KVOController會隨著觀察者的釋放而釋放润绎,KVOController在自己的dealloc函數(shù)中移除KVO監(jiān)聽撬碟,巧妙的將觀察者的remove轉(zhuǎn)移到其動態(tài)屬性的dealloc函數(shù)中。
可是莉撇,這又有什么用呢蛤?對象仍然無法監(jiān)聽自己的屬性,還是要重寫set函數(shù)棍郎。HTBKVObservation也許會改變你的想法其障,其和FBKVOController來自同一人,代碼可以這樣寫
self.anObservation=[HTBKVObservationobserve:anObjectToObservekeyPath:@"observeMe"options:0callback:^(HTBKVObservation*observation,NSDictionary*changeDictionary){
// to do
}];
HTBKVObservation并沒用采用動態(tài)屬性涂佃,而是采用屬性的方式實現(xiàn)自釋放励翼。可以監(jiān)控對象自己的屬性辜荠,但是需要創(chuàng)建屬性HTBKVObservation汽抚。這里我對其做了一點擴展,方便使用伯病,代碼可以這樣寫[selfobserve:selfkeyPath:@"KVOPath"options:NSKeyValueObservingOptionNewcallback:^(HTBKVObservation*observation,NSDictionary*changeDictionary){
// to do
}];
FBKVOController和HTBKVObservation通過屬性或動態(tài)屬性巧妙的將KVO的remove轉(zhuǎn)移給第三者造烁,實現(xiàn)了KVO事件的解耦,為自釋放的實現(xiàn)提供了一種借鑒思路
NSNotification的自釋放
談完 KVO狱从,再來談?wù)凬SNotification膨蛮。針對Notification叠纹,ReactiveCocoa做了很好的封裝季研,網(wǎng)上有很多介紹其如何使用的文章,在此不再累述誉察。直接看代碼
[[[NSNotificationCenterdefaultCenter]rac_addObserverForName:UIKeyboardDidChangeFrameNotificationobject:nil]subscribeNext:^(idx){
// to do
}
];
簡單明了与涡,當(dāng)觀察者dealloc,很遺憾持偏,NSNotification并沒用移除驼卖,因為對象并沒用自釋放,正確代碼應(yīng)該是這樣
1
2
3
4[[[[NSNotificationCenterdefaultCenter]rac_addObserverForName:UIKeyboardDidChangeFrameNotificationobject:nil]takeUntil:self.rac_willDeallocSignal]subscribeNext:^(idx){
// to do
}
];
ReactiveCocoa自釋放的原理與FBKVOController不同鸿秆,其并不是通過屬性或者動態(tài)屬性的方式實現(xiàn)酌畜,而是通過swizzling觀察對象的dealloc函數(shù),在自定義dealloc函數(shù)實施清理卿叽,但不是默認清理桥胞,需要我們告訴它willDeallocSignal的時候完成所有清理工作恳守。
除了定時器、KVO贩虾、NSNotification催烘,包括封裝的某個功能對象,比如HttpRequest缎罢,或者數(shù)據(jù)庫ListSql等伊群,合理的利用自釋放可以給使用者帶來更多的便利,同時也會減少 crash 產(chǎn)生的概率策精。實現(xiàn)自釋放的方法可以總結(jié)為以下三種方式
動態(tài)屬性的自釋放
@property 的自釋放
swizzling dealloc的自釋放
可以根據(jù)具體業(yè)務(wù)或者設(shè)計思想選擇對應(yīng)的實現(xiàn)方式舰始,這是一種思想,更是一個習(xí)慣咽袜。相信你會愛上它蔽午,畢竟誰不喜歡簡潔的實現(xiàn)方式了!