一、內(nèi)存泄漏的原因
1点额、iOS中引入ARC(Automatic Reference Counting)機(jī)制后還是會內(nèi)存泄漏履羞。主要原因是程序中存在循環(huán)強(qiáng)引用枣申,導(dǎo)致對象自動釋放時(shí)出現(xiàn)相互等待不能及時(shí)釋放內(nèi)存(不進(jìn)行及時(shí)處理一直會等到整個(gè)程序結(jié)束時(shí)才釋放)。解決辦法:打破循環(huán)引用鏈饮亏。
2耍贾、ARC只會對Foundation\UIKit(前綴NS__、UI__)框架下的對象進(jìn)行自動管理路幸。在使用Core Foundation(前綴CF__)框架下的對象時(shí)還是需要去手動調(diào)用CFRelease(CF__荐开、CG__)管理。
二简肴、內(nèi)存泄漏的幾種情況
1晃听、Block:
在 ARC 下,當(dāng) block 獲取到外部變量時(shí)砰识,由于編譯器無法預(yù)測獲取到的變量何時(shí)會被突然釋放能扒,為了保證程序能夠正確運(yùn)行,讓 block 持有獲取到的變量辫狼,向系統(tǒng)聲明:我要用它初斑,你們千萬別把它回收了!然而予借,也正因 block 持有了變量越平,容易導(dǎo)致變量和 block 的循環(huán)引用,造成內(nèi)存泄露灵迫。
2秦叛、NSTimer:
NSTimer在釋放前,一定要調(diào)用[timer invalidate]瀑粥,不調(diào)用的后果就是NSTimer無法釋放其target挣跋,如果target正好是self,則會導(dǎo)致引用循環(huán)狞换。
3避咆、Delegate:
我們在使用代理設(shè)計(jì)模式的時(shí)候,一定要注意將 delegate 變量聲明為 weak 類型修噪,像這樣如使用strong或別的類型修飾的話將會導(dǎo)致循環(huán)引用查库。
三、如何快速檢測VC中的內(nèi)存泄漏 ---(重點(diǎn);魄怼7!)
一般我們都是通過重寫dealloc方法來檢測當(dāng)前控制器內(nèi)是否出現(xiàn)內(nèi)存泄漏,如果每一個(gè)控制器中都去寫一遍dealloc是非常較麻煩的围苫。所以我準(zhǔn)備給UIViewController裤园、UINavigationController添加分類的方式來實(shí)現(xiàn),先上代碼(可先看后面的總結(jié)):
先給UIViewController加分類:
@interface UIViewController (LeaksTest)
@end
#import "UIViewController+LeaksTest.h"
#import <objc/message.h>
const char * ZJPOPFLAG;
@implementation UIViewController (LeaksTest)
+ (void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//替換類方法
[self zj_swizzleOriginSEL:@selector(viewWillAppear:) currentSEL:@selector(zj_viewWillAppear:)];
[self zj_swizzleOriginSEL:@selector(viewDidDisappear:) currentSEL:@selector(zj_viewDidDisappear:)];
});
}
/**方法交換*/
+ (void)zj_swizzleOriginSEL:(SEL)originSEL currentSEL:(SEL )currentSEL{
Method originMethod = class_getInstanceMethod([self class], originSEL);
Method currentMethod = class_getInstanceMethod([self class], currentSEL);
method_exchangeImplementations(originMethod, currentMethod);
}
//自定義viewWillAppear
- (void)zj_viewWillAppear:(BOOL)animated{
[self zj_viewWillAppear:animated];
objc_setAssociatedObject(self, &ZJPOPFLAG, @(NO), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
//自定義viewDidDisappear
- (void)zj_viewDidDisappear:(BOOL)animated{
[self zj_viewDidDisappear:animated];
if ([objc_getAssociatedObject(self, &ZJPOPFLAG) boolValue]) {
[self willDealloc];
}
}
/**即將都用dealloc*/
- (void)willDealloc{
__weak typeof(self) weakSelf = self;
//延時(shí)4s 留足釋放內(nèi)存時(shí)間
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)( 4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
__strong typeof(self) strongSelf = weakSelf;
/* !!!重要點(diǎn) !!!
objc_msgSend(strongSelf,@selector(isNotDealloc));
如果控制器銷毀就不會執(zhí)行剂府,如果執(zhí)行說明控制器沒有被釋放(可能出現(xiàn)內(nèi)存泄漏)
*/
[strongSelf isNotDealloc];
});
}
/**控制器未釋放拧揽,內(nèi)存泄漏*/
- (void)isNotDealloc{
NSLog(@"%@ is not dealloc",self);
}
@end
再給UINavigationController加分類:
@interface UINavigationController (LeaksTet)
@end
#import "UINavigationController+LeaksTet.h"
#import <objc/message.h>
@implementation UINavigationController (LeaksTet)
+ (void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method originMethod = class_getInstanceMethod([self class], @selector(popViewControllerAnimated:));
Method currentMethod = class_getInstanceMethod([self class], @selector(zj_popViewControllerAnimated:));
//替換POP方法
method_exchangeImplementations(originMethod, currentMethod);
});
}
/**自定義popViewControllerAnimated*/
- (UIViewController *)zj_popViewControllerAnimated:(BOOL)animated{
UIViewController * popVC = [self zj_popViewControllerAnimated:animated];
extern const char * ZJPOPFLAG;
//給ZJPOPFLAG標(biāo)識賦值
objc_setAssociatedObject(popVC, &ZJPOPFLAG, @(YES), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
return popVC;
}
@end
總結(jié):
-
1、自定義控制器三個(gè)系統(tǒng)方法 :zj_viewWillAppear腺占、zj_viewDidDisappear淤袜、zj_popViewControllerAnimated;
-
2、通過Runtime在+load方法中替換系統(tǒng)中的三個(gè)方法:viewWillAppear湾笛、viewDidDisappear饮怯、popViewControllerAnimated
替換方法:method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2)
-
3、在3個(gè)自定義的方法中給當(dāng)前VC添加一個(gè)關(guān)聯(lián)標(biāo)識(==添加屬性)嚎研。
關(guān)聯(lián)方法:
設(shè)值:objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key, id _Nullable value, objc_AssociationPolicy policy)
取值:objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)
然后通過該標(biāo)識值來判斷當(dāng)前控制器是否要退出(銷毀)蓖墅,在即將退出時(shí)調(diào)用-willDealloc來執(zhí)行延時(shí)任務(wù)。
(1)临扮、若控制器銷毀 [strongSelf isNotDealloc]將不會執(zhí)行打印任務(wù);
(2)论矾、若控制器未銷毀 [strongSelf isNotDealloc]將打印出:
<ViewControllerB: 0x7fbc96415bf0> is not dealloc
到此一個(gè)簡單的自動檢測控制器內(nèi)存泄漏的工具就完成了,使用時(shí)只需要添加分類頭文件即可杆勇,具體代碼我就不寫了贪壳。(見諒!)