前言
由循環(huán)引用導(dǎo)致的內(nèi)存泄漏是常出現(xiàn)的一個(gè)原因。
一般都是weak給弱化一方的指針结啼,打破循環(huán)引用劈猿。一些隱藏的循環(huán)引用還是不易發(fā)現(xiàn)的。
接下來就用一個(gè)小例子來聊聊循環(huán)引用庸蔼。
案例
在controller中有:
[ZJHttpManager requestName:^(NSString * _Nonnull name) {
self.name = name;
NSLog(@"name is %@",name);
}];
模擬數(shù)據(jù)請求過程。如果在ZJHttpManager的requestName:方法中沒有對block引用贮匕,也就沒有對self進(jìn)行引用朱嘴,在釋放controller時(shí)是會執(zhí)行dealloc
的。
//ZJHttpManager.h
@interface ZJHttpManager : NSObject
+ (void)requestName:(void(^)(NSString *name))successBlock;
@end
//ZJHttpManager.m
#import "ZJHttpManager.h"
typedef void (^ MyBlock)(NSString *name);
@interface ZJHttpManager()
@property(nonatomic, strong,nullable) NSTimer * timer;
@property(nonatomic, copy) MyBlock block;
@end
@implementation ZJHttpManager
- (instancetype)init{
if (self = [super init]) {
self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(run) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop]addTimer:self.timer forMode:NSRunLoopCommonModes];
}
return self;
}
- (void)run{
NSLog(@"___%@",NSStringFromSelector(_cmd));
}
+ (void)requestName:(void(^)(NSString *name))successBlock{
ZJHttpManager * manager = [[ZJHttpManager alloc]init];
manager.block = successBlock;
if (successBlock) {
successBlock(@"小明");
}
}
@end
本示例中是用NSTimer來制造出循環(huán)引用粗合,在開發(fā)過程中可能是由于其他情況造成的萍嬉,需具體分析。
timer在未釋放時(shí)可以一直打印隙疚,也可從是否打印看出是否釋放了timer壤追。
有內(nèi)存泄漏 未dealloc
[ZJHttpManager requestName:^(NSString * _Nonnull name) {
self.name = name;
NSLog(@"name is %@",name);
}];
當(dāng)ZJHttpManager的block直接引用self的情況下,self是沒有執(zhí)行dealloc進(jìn)行釋放的供屉。
執(zhí)行dealloc
雖然執(zhí)行了dealloc,釋放了self了行冰,但是未循環(huán)引用未打破,還是存在內(nèi)存泄漏的隱患伶丐。接下來看具體分析悼做。
__weak typeof(self) weakSelf = self;
[ZJHttpManager requestName:^(NSString * _Nonnull name) {
weakSelf.name = name;
NSLog(@"name is %@",name);
}];
self被weak弱化下,可以得出結(jié)論會執(zhí)行dealloc,self是可以被釋放了哗魂,但是此時(shí)timer還一直在打印肛走,并未得到釋放。
到此可以得出結(jié)論录别,ZJHttpManager類方法法中有一個(gè)循環(huán)引用朽色,即manager對象引用timer邻吞,而timer的target是manager對象,也引用manager,造成一個(gè)循環(huán)體。這個(gè)循環(huán)體中的manager強(qiáng)引用著controller葫男。所以在該釋放controller的時(shí)候并未執(zhí)行dealloc進(jìn)行釋放抱冷。
此時(shí)弱化self(即當(dāng)前controller),也僅僅是弱化了循環(huán)體對controller的引用,并未打破真正的循環(huán)體。仍會造成內(nèi)存泄漏梢褐。
正確處理方式
應(yīng)該在適當(dāng)?shù)牡胤秸嬲蚱蒲h(huán)引用旺遮。本案例中,應(yīng)打破timer和manager的循環(huán),在適當(dāng)?shù)牡胤结尫舤imer盈咳。
總結(jié):在實(shí)際開發(fā)中趣效,隱形的循環(huán)引用是導(dǎo)致內(nèi)存泄漏的一個(gè)重要原因,可以在工程中集成查找內(nèi)存泄漏的工具猪贪,進(jìn)而發(fā)現(xiàn)內(nèi)存泄漏的隱患。
測試小demo