前言:本文旨在介紹iOS性能優(yōu)化中有關(guān)內(nèi)存泄漏的介紹和檢測。
一衡蚂、什么是內(nèi)存泄漏窿克?
內(nèi)存泄漏是指申請的內(nèi)存空間使用完成之后沒有及時的回收骏庸,導(dǎo)致頁面不釋放,從而加劇了內(nèi)存的暴增年叮。
二具被、常見的內(nèi)存泄漏
1、Block的循環(huán)應(yīng)用
一個對象持有Block只损,同時Block內(nèi)部又持有這個對象一姿,會導(dǎo)致循環(huán)應(yīng)用,相互不釋放跃惫。外部可以使用__weak來進(jìn)行解決叮叹。
2、代理delegate用strong修飾
代理delegate用strong修飾同樣會造成內(nèi)存泄漏爆存≈孟牛可以使用weak修飾delegate來解決這個問題和二。
3日熬、NSTimer定時器的使用
3.1签则、創(chuàng)建定時器使用了帶有target的方法
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
上面的方法創(chuàng)建的NSTimer,會使其不能釋放拇泣。可以使用以下兩種方式解決:
- 方式一:設(shè)置target時矮锈,使用NSProxy來解決霉翔,詳情可參考YYKit中的使用創(chuàng)建方式:
@interface YYWeakProxy : NSProxy
@property (nullable, nonatomic, weak, readonly) id target;
- (instancetype)initWithTarget:(id)target;
+ (instancetype)proxyWithTarget:(id)target;
@end
self.timer = [NSTimer timerWithTimeInterval:1 target:[YYWeakProxy proxyWithTarget:self] selector:@selector(setupCountAction) userInfo:nil repeats:YES];
備注:NSProxy也是一個基類,和NSObject同級別的基類苞笨。主要用來做消息轉(zhuǎn)發(fā)债朵。
- 方式二:直接使用帶有block的創(chuàng)建方式:
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
- (instancetype)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
不過以上方法都是iOS10才出現(xiàn)的API,使用時需要注意瀑凝。
3.2序芦、創(chuàng)建的定時器在頁面消失后沒有及時的對Timer進(jìn)行銷毀
一般情況下,我們會在定時器結(jié)束后去銷毀定時器粤咪。但出現(xiàn)以下情況則會出現(xiàn)無法銷毀定時器的問題:
- 情況一:當(dāng)前頁面定時器還未結(jié)束谚中,而頁面已經(jīng)返回上一頁;
- 情況一:當(dāng)前頁面定時器還未結(jié)束寥枝,而此時跳轉(zhuǎn)到下一頁宪塔,由于一些操作最后并沒有回到這個頁面,而此時的定時器還在執(zhí)行囊拜。
解決辦法:可以在頁面的 viewDidAppear 和 viewDidDisappear 的兩個方法中根據(jù)具體的情況去創(chuàng)建和銷毀定時器某筐。這樣在頁面消失的時候就可以及時的銷毀定時器了。
三冠跷、內(nèi)存泄露檢測工具:
1南誊、騰訊 MLeaksFinder
1身诺、優(yōu)點和缺點
- 優(yōu)點:對項目無侵入。將MLeaksFinder 引入到項目中抄囚,操作項目頁面霉赡,有內(nèi)存泄漏問題,則會彈窗提示怠苔。
- 缺點:目前只能對相關(guān)Controller同廉、UIView作出檢測,對于其他類則無法作出檢測柑司。
2迫肖、檢測原理:
首先通過分類的方式給基類NSObject添加一個 willDealloc 的方法,該方法中會生成一個弱指針指向weakSelf攒驰,并在2秒之后 weakSelf 會調(diào)用彈窗提示方法 assertNotDealloc蟆湖。然后通過方法交換的方式,當(dāng)VC 執(zhí)行pop 或 dismiss 后玻粪,在該方法中去執(zhí)行willDealloc方法隅津。若pop 或 dismiss后對象被銷毀,dealloc執(zhí)行后weakSelf會指向nil劲室,就不會調(diào)用assertNotDealloc方法伦仍。反之,就會調(diào)用彈窗很洋,給出提示充蓝。
2、騰訊 OOMDetector
具體檢測內(nèi)容:
- OOM監(jiān)控:監(jiān)控OOM喉磁,Dump引起爆內(nèi)存的堆棧
- 大內(nèi)存分配監(jiān)控:監(jiān)控單次大塊內(nèi)存分配谓苟,提供分配堆棧信息
- 內(nèi)存泄漏檢測:可檢測OC對象、Malloc堆內(nèi)存泄漏协怒,提供泄漏堆棧信息
3涝焙、Facebook的 FBRetainCycleDetector
檢測原理:
對所有檢測到的強引用變量,利用DFS(深度優(yōu)先搜索)孕暇,采用stack棧的形式仑撞,將遍歷到的對象依次入棧,如果某次入棧的對象已經(jīng)存在stack中妖滔,說明該對象在stack中的位置直到當(dāng)前為止派草,存在引用環(huán)。