ios中循環(huán)引用問(wèn)題
ARC自動(dòng)釋放內(nèi)存的確很方便,但是并非絕對(duì)安全絕對(duì)不會(huì)產(chǎn)生內(nèi)存泄露淋叶。導(dǎo)致iOS對(duì)象無(wú)法按預(yù)期釋放的一個(gè)無(wú)形殺手是——循環(huán)引用阎曹。循環(huán)引用主要有以下3個(gè)情形出現(xiàn):
NO1: NSTimer
問(wèn)題:當(dāng)你創(chuàng)建使用NSTimer的時(shí)候,NSTimer會(huì)默認(rèn)對(duì)當(dāng)前self有個(gè)強(qiáng)引用,所有在self使用完成打算是否的時(shí)候处嫌,一定要先使用NSTimer的invalidate來(lái)停止是否時(shí)間控制對(duì)self的引用
#import <Foundation/Foundation.h>
@interface Student : NSObject
- (void)cleanTimer;
@end
#import "Student.h"
@interface Student () {
NSTimer *_timer;
}
@end
@implementation Student
- (id)init{
if (self = [super init]) {
_timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(handleTimer:) userInfo:nil repeats:YES];
}
return self;
}
- (void)handleTimer:(id)sender{
NSLog(@"%@ say: Hi!", [self class]);
}
- (void)cleanTimer {
[_timer invalidate];
_timer = nil;
}
//time角度栅贴,當(dāng)student被釋放時(shí) 銷(xiāo)毀timer
//student角度 當(dāng)timer停止時(shí)銷(xiāo)毀 這樣兩者形成相互不銷(xiāo)毀 循環(huán)引用,互相等待
- (void)dealloc{
[self cleanTimer];
NSLog(@"[Student class] is dealloced");
}
@end
#import "ViewController.h"
#import "Student.h"
@interface ViewController ()
@en
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Student *stu = [[Student alloc] init];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5*NSEC_PER_SEC), dispatch_get_main_queue(), ^{
//釋放stu [stu release]; 錯(cuò)誤方式:執(zhí)行到此會(huì)判斷stu對(duì)象是否還有持有的對(duì)象熏迹,若還有則不被釋放 (NStimer)
[stu cleanTimer]; //確定不用的時(shí)候主動(dòng)銷(xiāo)毀 正確方式
});
}
NO2:Block
Block也是比較常見(jiàn)的循環(huán)引用問(wèn)題檐薯,在Block中使用了self容易出現(xiàn)循環(huán)引用,因此很多人在使用block的時(shí)候注暗,加入里面有用到self的操作都會(huì)聲明一個(gè)__weak來(lái)修飾self坛缕。其實(shí)便不是這樣的,不是所有使用了Block都會(huì)出現(xiàn)Self循環(huán)引用問(wèn)題捆昏,只有self擁有Block的強(qiáng)引用才會(huì)出現(xiàn)這種情況赚楚。Block在copy時(shí)都會(huì)對(duì)Block內(nèi)部用到的對(duì)象進(jìn)行強(qiáng)引用(ARC)或者retainCount增1(非ARC)。在ARC與非ARC環(huán)境下對(duì)block使用不當(dāng)都會(huì)引起循環(huán)引用問(wèn)題骗卜,一般表現(xiàn)為宠页,某個(gè)類(lèi)將block作為自己的屬性變量,然后該類(lèi)在block的方法體里面又使用了該類(lèi)本身.
self.arr = @[@111, @222, @333];
self.block = ^(NSString *name){
NSLog(@"arr:%@", self.arr);//不一定要顯式地出現(xiàn)"self"字眼才會(huì)引起循環(huán)引用
};
//修改一下代碼
self.arr = @[@111, @222, @333];
self.block = ^(NSString *name){
NSLog(@"_arr:%@", _arr);//不通過(guò)屬性self.arr去訪(fǎng)問(wèn)arr變量寇仓,而是通過(guò)實(shí)例變量_arr去訪(fǎng)問(wèn) 一樣會(huì)提示 循環(huán)引用問(wèn)題
};
通過(guò)如下方法解決:
__weak __typeof__(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[weakSelf doSomething]举户;//
[weakSelf doOtherThing]; //在 doSomething 中,weakSelf 不會(huì)變成 nil焚刺,不過(guò)在 doSomething 執(zhí)行完成敛摘,調(diào)用第二個(gè)方法 doOtherThing 的時(shí)候,weakSelf 有可能被釋放乳愉,于是,strongSelf 就派上用場(chǎng)了:
/*__strong __typeof(self) strongSelf = weakSelf;*/
});
總結(jié)
1 在 Block 內(nèi)如果需要訪(fǎng)問(wèn) self 的方法屯远、變量蔓姚,建議使用 weakSelf。
2 如果在 Block 內(nèi)需要多次 訪(fǎng)問(wèn) self慨丐,則需要使用 strongSelf坡脐。
NO3:delegate
Delegate是ios中開(kāi)發(fā)中最常遇到的循環(huán)引用,一般在聲明delegate的時(shí)候都要使用弱引用weak或者assign
@property (nonatomic, weak, nullable) id <UITableViewDelegate> delegate;
當(dāng)然怎么選擇使用assign還是weak房揭,MRC的話(huà)只能用assign备闲,在ARC的情況下最好使用weak,因?yàn)閣eak修飾的變量在是否后自動(dòng)為指向nil捅暴,防止不安全的野指針存在.