前言
由循環引用導致的內存泄漏是常出現的一個原因。
一般都是weak給弱化一方的指針,打破循環引用。一些隱藏的循環引用還是不易發現的。
接下來就用一個小例子來聊聊循環引用。
案例
在controller中有:
[ZJHttpManager requestName:^(NSString * _Nonnull name) {
self.name = name;
NSLog(@"name is %@",name);
}];
模擬數據請求過程。如果在ZJHttpManager的requestName:方法中沒有對block引用,也就沒有對self進行引用,在釋放controller時是會執行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來制造出循環引用,在開發過程中可能是由于其他情況造成的,需具體分析。
timer在未釋放時可以一直打印,也可從是否打印看出是否釋放了timer。
有內存泄漏 未dealloc
[ZJHttpManager requestName:^(NSString * _Nonnull name) {
self.name = name;
NSLog(@"name is %@",name);
}];
當ZJHttpManager的block直接引用self的情況下,self是沒有執行dealloc進行釋放的。
執行dealloc
雖然執行了dealloc,釋放了self了,但是未循環引用未打破,還是存在內存泄漏的隱患。接下來看具體分析。
__weak typeof(self) weakSelf = self;
[ZJHttpManager requestName:^(NSString * _Nonnull name) {
weakSelf.name = name;
NSLog(@"name is %@",name);
}];
self被weak弱化下,可以得出結論會執行dealloc,self是可以被釋放了,但是此時timer還一直在打印,并未得到釋放。
到此可以得出結論,ZJHttpManager類方法法中有一個循環引用,即manager對象引用timer,而timer的target是manager對象,也引用manager,造成一個循環體。這個循環體中的manager強引用著controller。所以在該釋放controller的時候并未執行dealloc進行釋放。
此時弱化self(即當前controller),也僅僅是弱化了循環體對controller的引用,并未打破真正的循環體。仍會造成內存泄漏。
正確處理方式
應該在適當的地方真正打破循環引用。本案例中,應打破timer和manager的循環,在適當的地方釋放timer。
總結:在實際開發中,隱形的循環引用是導致內存泄漏的一個重要原因,可以在工程中集成查找內存泄漏的工具,進而發現內存泄漏的隱患。
測試小demo