文章背景
在很多的時(shí)候 , 我們項(xiàng)目中的控制器會(huì)延遲釋放 ,為了了解為什么會(huì)延遲釋放和延遲釋放的解決方式特制作了一個(gè)demo, 項(xiàng)目中延遲釋放會(huì)帶來很多問題, 因?yàn)檠舆t師范項(xiàng)目中的資源清理不敢放在dealloc 中 進(jìn)行, 因?yàn)橐坏┭舆t釋放了, 可能存在已經(jīng)在viewdidload 修改的變量, 被延遲釋放的控制器將數(shù)據(jù)修改了
延遲釋放的原因
根據(jù)蘋果官方文檔中對(duì) NSAutoreleasePool 的描述毫炉,我們可知竞穷,在主線程的 NSRunLoop 對(duì)象(在系統(tǒng)級(jí)別的其他線程中應(yīng)該也是如此坷剧,比如通過 dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) 獲取到的線程)的每個(gè) event loop 開始前脑沿,系統(tǒng)會(huì)自動(dòng)創(chuàng)建一個(gè) autoreleasepool ,并在 event loop 結(jié)束時(shí) drain 麻诀。我們推出一個(gè)控制器的時(shí)候 中創(chuàng)建的 autoreleased 對(duì)象就是被系統(tǒng)添加到了這個(gè)自動(dòng)創(chuàng)建的 autoreleasepool 中,并在這個(gè) autoreleasepool 被 drain 時(shí)得到釋放。如果有定時(shí)器的任務(wù)沒有執(zhí)行完, 是不會(huì)調(diào)用drain的 , 知道定時(shí)器事件執(zhí)行完, 才會(huì)調(diào)用drain 來釋放控制器 ,如果定時(shí)器一致在行走, 對(duì)象是不釋放的, 除非使用weak
本文所述的延遲釋放情形在release 模式下也會(huì)存在延遲釋放
延遲釋放情形一)
dispatch_after , dispatch_after延時(shí)N秒, 控制器就會(huì)在N秒之后釋放
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// for(int i = 0;i< 10;i++){
// [weakSelf requst:@"http://forspeed.onlinedown.net/down/YJPDFViewer2.0.zip"];
// }
// });
解決方案1) - weakSelf
// 方案一 weak 下 會(huì) 可以解決延遲釋放
__weak typeof(self) weakSelf = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
for(int i = 0;i< 10;i++){
[weakSelf requst:@"http://forspeed.onlinedown.net/down/YJPDFViewer2.0.zip"];
}
});
解決方案2) weak strong
weak strong 的代碼
#ifndef weakify
#if DEBUG
#if __has_feature(objc_arc)
#define weakify(object) autoreleasepool{} __weak __typeof__(object) weak##_##object = object;
#else
#define weakify(object) autoreleasepool{} __block __typeof__(object) block##_##object = object;
#endif
#else
#if __has_feature(objc_arc)
#define weakify(object) try{} @finally{} {} __weak __typeof__(object) weak##_##object = object;
#else
#define weakify(object) try{} @finally{} {} __block __typeof__(object) block##_##object = object;
#endif
#endif
#endif
#ifndef strongify
#if DEBUG
#if __has_feature(objc_arc)
#define strongify(object) autoreleasepool{} __typeof__(object) object = weak##_##object;
#else
#define strongify(object) autoreleasepool{} __typeof__(object) object = block##_##object;
#endif
#else
#if __has_feature(objc_arc)
#define strongify(object) try{} @finally{} __typeof__(object) object = weak##_##object;
#else
#define strongify(object) try{} @finally{} __typeof__(object) object = block##_##object;
#endif
#endif
#endif
// 方案2 weak strong 可以解決延遲釋放 為加 weak strong 會(huì)延遲釋放
// @weakify(self)
// dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// @strongify(self)
// for(int i = 0;i< 10;i++){
// [self requst:@"http://forspeed.onlinedown.net/down/YJPDFViewer2.0.zip"];
// }
// });
延遲釋放情形二)
這種定時(shí)器會(huì)引起延時(shí)釋放 , 如果非repeat 的話 直到 5 秒之后才會(huì)釋放 , 如果是repeat 的話 控制器 會(huì)一致導(dǎo)致控制器不釋放
// 這種定時(shí)器會(huì)引起延時(shí)釋放 , 如果非repeat 的話 直到 5 秒之后才會(huì)釋放 , 如果是repeat 的話 控制器 會(huì)一致導(dǎo)致控制器不釋放
// _timer = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:@selector(handleTimer:)
// userInfo:nil repeats:NO];
問題分析:
原因是 Timer 添加到 Runloop 的時(shí)候洒宝,會(huì)被 Runloop 強(qiáng)引用,然后 Timer 又會(huì)有一個(gè)對(duì) Target 的強(qiáng)引用(也就是 self )也就是說 NSTimer 強(qiáng)引用了 self 萌京,導(dǎo)致 self 一直不能被釋放掉雁歌,所以 self 的 dealloc 方法也一直未被執(zhí)行.
知道了錯(cuò)誤原因,就先查一下NSTimer的官方文檔看看具體用法細(xì)節(jié)知残,發(fā)現(xiàn)NSTimer還有一個(gè)規(guī)則:(在哪個(gè)線程創(chuàng)建就要在哪個(gè)線程停止靠瞎,否則會(huì)導(dǎo)致資源不能被正確的釋放。)那么問題來了:如果我就是想讓這個(gè) NSTimer 一直輸出,直到控制器 銷毀才停止并且釋放NSTimer乏盐。
問題關(guān)鍵:
問題的關(guān)鍵就在于 self 被 NSTimer 強(qiáng)引用了佳窑,如果能打破這個(gè)強(qiáng)引用,問題應(yīng)該就能決了父能。
解決方案:
使用weaktimer, HWWeakTimer , 不僅可以避免循環(huán)引用, 還可以解決延遲釋放
HWWeakTimer的源碼在本文中的demo中
// 這里使用了weaktimer 之后 不管 是否repeat 控制器都可以及時(shí)的釋放
_timer = [HWWeakTimer scheduledTimerWithTimeInterval:5 target:self selector:@selector(handleTimer:)
userInfo:nil repeats:YES];
延遲釋放情形三)
此時(shí)準(zhǔn)確的說控制器未被釋放 是控制器被循環(huán)引用了, 這是一種很常見的情形, 此處不贅述
不會(huì)導(dǎo)致延遲釋放但是會(huì)導(dǎo)致對(duì)象不釋放的情形
// 這里的不會(huì)引起延遲釋放 但是會(huì)導(dǎo)致 a 對(duì)象和b 對(duì)象不釋放 , 當(dāng)前的控制器對(duì)a b 對(duì)象無引用, 但是a b 對(duì)象內(nèi)部會(huì)形成引用環(huán)
ClassA *a = [[ClassA alloc] init];
ClassB *b = [[ClassB alloc] init];
a.classb = b;
b.classa = a;