此處記錄的都是ARC情況下~~~
循環(huán)引用是什么抛猖?
當(dāng)兩個(gè)不同的對(duì)象各有一個(gè)強(qiáng)引用指向?qū)Ψ浇冢敲囱h(huán)引用便產(chǎn)生了稻爬,當(dāng)然多一個(gè)對(duì)象產(chǎn)生的環(huán)也是一樣的。
也可以直接從引用計(jì)數(shù)說(shuō)明矛辕,此時(shí)如果互相引用的時(shí)候,雙方的引用計(jì)數(shù)都是+1的漓柑,導(dǎo)致任何時(shí)候引用計(jì)數(shù)都不為0竟宋,始終無(wú)法釋放,無(wú)法釋放他們的內(nèi)存粘捎,即使已經(jīng)沒(méi)有變量持有他們薇缅。
循環(huán)引用帶來(lái)什么危害
循環(huán)引用對(duì) app 有潛在的危害危彩,會(huì)使內(nèi)存消耗過(guò)高,性能變差和 app 閃退等捅暴。
循環(huán)引用有哪些具體的情況恬砂?block ? delegate ? NSTimer?
1蓬痒、父類(lèi)與子類(lèi)
例如泻骤,我們?cè)谑褂肬ITableView 的時(shí)候,傳一個(gè) Controller 給 Cell 使用
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TestTableViewCell *cell =[tableView dequeueReusableCellWithIdentifier:@"UITableViewCellIden" forIndexPath:indexPath];
cell.tableView = tableView;
return cell;
}
@interface TestTableViewCell : UITableViewCell
@property (nonatomic, weak) UITableView *tableView;
@end
此時(shí)如果我們給 tableView 的屬性特質(zhì)
strong
就會(huì)用造成循環(huán)引用的梧奢,UITableView ==> UITableViewCell; UITableViewCell ==> UITableView 狱掂。因此weak
是肯定的。
2亲轨、Block
Block 是我們常用到的趋惨,也是我們最要注意循環(huán)引用的。在此惦蚊,先拿一個(gè)我們使用 Block 的例子:
typedef void (^TestCircleBlock)();
@property (nonatomic, copy) TestCircleBlock testCircleBlock;
if (self.testCircleBlock) {
// 具體實(shí)現(xiàn)
self.testCircleBlock();
}
block
在copy
時(shí)都會(huì)對(duì)block
內(nèi)部用到的對(duì)象進(jìn)行強(qiáng)引用的器虾。
- 該類(lèi)又將
block
作為自己的屬性變量,而該類(lèi)在block的方法體里面又使用了該類(lèi)本身蹦锋,此時(shí)就很簡(jiǎn)單的形成了一個(gè)環(huán)啦兆沙。
self.testObject.testCircleBlock = ^{
[self doSomething];
};
進(jìn)行弱引用后,打破了循環(huán)莉掂,我們可以這樣:
__weak typeof(self) weakSelf = self;
self.testObject.testCircleBlock = ^{
__strong typeof (weakSelf) strongSelf = weakSelf;
[strongSelf doSomething];
};
在 ARC 中葛圃,在被拷貝的 block 中無(wú)論是直接引用 self 還是通過(guò)引用 self 的成員變量間接引用 self,該 block 都會(huì) retain self憎妙。
但是注意 block 里面直接用“成員變量”的情況库正,有些情況下我們是沒(méi)法直接撲捉到 間接引用的那個(gè)self,或者說(shuō)撲捉到的那個(gè)self 是不對(duì)的,也就沒(méi)法對(duì)其進(jìn)行weak引用啦厘唾,循環(huán)依然存在褥符,所以建議直接用屬性!
總的說(shuō)來(lái)抚垃,block 我們很有必要深入了解喷楣,了解為什么用 copy,了解它在內(nèi)存中的位置,更有利于我們理解的讯柔。Block基礎(chǔ)和retain cycle(循環(huán)引用)
3抡蛙、Delegate
相信類(lèi)似下面這樣的代理屬性,我們寫(xiě)了不要太多遍了吧
@property (nonatomic, weak) id <TestDelegate> delegate;
說(shuō)白了就是循環(huán)使用的問(wèn)題魂迄,假如我們是寫(xiě)的strong
,那么 兩個(gè)類(lèi)之間調(diào)用代理就是這樣的啦
BViewController *bViewController = [[BViewController alloc] init];
bViewController.delegate = self; //假設(shè) self 是AViewController
[self.navigationController pushViewController:bViewController animated:YES];
/**
假如是 strong 的情況
bViewController.delegate ===> AViewController (也就是 A 的引用計(jì)數(shù) + 1)
AViewController 本身又是引用了 <BViewControllerDelegate> ===> delegate 引用計(jì)數(shù) + 1
導(dǎo)致: AViewController <======> Delegate 粗截,也就循環(huán)引用啦
*/
- Delegate創(chuàng)建并強(qiáng)引用了 AViewController;(
strong ==> A 強(qiáng)引用捣炬、weak ==> 引用計(jì)數(shù)不變
)
所以用
strong
的情況下熊昌,相當(dāng)于 Delegate 和 A 兩個(gè)互相引用啦绽榛,A 永遠(yuǎn)會(huì)有一個(gè)引用計(jì)數(shù) 1 不會(huì)被釋放,所以造成了永遠(yuǎn)不能被內(nèi)存釋放婿屹,因此weak
是必須的灭美。
4、NSTimer
NSTimer 其實(shí)相對(duì)來(lái)說(shuō)昂利,我們其實(shí)是很容易忽略它這種情況的届腐,畢竟還是很特殊的。
此時(shí)解決的方法還是用 “Effective Objective-C ”中的52條方法
#import <Foundation/Foundation.h>
@interface NSTimer (YPQBlocksSupport)
+ (NSTimer *)ypq_scheduledTimeWithTimeInterval:(NSTimeInterval)interval
block:(void(^)())block
repeats:(BOOL)repeats;
@end
#import "NSTimer+YPQBlocksSupport.h"
@implementation NSTimer (YPQBlocksSupport)
+ (NSTimer *)ypq_scheduledTimeWithTimeInterval:(NSTimeInterval)interval
block:(void(^)())block
repeats:(BOOL)repeats
{
return [self scheduledTimerWithTimeInterval:interval
target:self
selector:@selector(ypq_blockInvoke:) userInfo:[block copy]
repeats:repeats];
}
- (void)ypq_blockInvoke:(NSTimer *)timer
{
void (^block)() = timer.userInfo;
if(block)
{
block();
}
}
@end
具體使用
__weak ViewController * weakSelf = self;
[NSTimer ypq_scheduledTimeWithTimeInterval:4.0f
block:^{
ViewController * strongSelf = weakSelf;
[strongSelf afterThreeSecondBeginAction];
}
repeats:YES];
計(jì)時(shí)器保留其目標(biāo)對(duì)象蜂奸,反復(fù)執(zhí)行任務(wù)導(dǎo)致的循環(huán)犁苏,確實(shí)要注意,另外在dealloc的時(shí)候扩所,不要忘了調(diào)用計(jì)時(shí)器中的 invalidate方法围详。
如何避免循環(huán)引用?理解上面幾種情況的發(fā)生的情況祖屏,我們一般就不會(huì)造成循環(huán)引用啦助赞,反正永遠(yuǎn)遵循,不要讓對(duì)象不能被釋放袁勺,誰(shuí)創(chuàng)建誰(shuí)釋放雹食!