1 循环引用
不建议使用:
2 使用单例的的一些情况
在使用单例的时候要注意,特别是单例含有block回调方法时候。有些单例会强持有这些block。这种情况虽然不是循环引用,但也是造成了喜欢引用。所以在使用单例的时候要清楚。如系统有些方法这样使用会造成无法释放:
- (void)viewDidLoad {
[super viewDidLoad];
self.obser = [[NSNotificationCenter defaultCenter] addObserverForName:@"boyce" object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
self.name = @"boyce";
}];
}
- (void)dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:self.obser];
}
这里就造成了内存泄漏,这是因为NSNotificationCenter强引用了usingBlock,而usingBlock强引用了self,而NSNotificationCenter是个单例不会被释放,而self在被释放的时候才会去把self.obser从NSNotificationCenter中移除。类似的情况还有很多,比如一个数组中对象等等。这些内存泄漏不容易发现。
3 NSTimer
NSTimer的target持有self
NSTimer会造成循环引用,timer会强引用target即self,一般self又会持有timer作为属性,这样就造成了循环引用。
那么,如果timer只作为局部变量,不把timer作为属性呢?同样释放不了,因为在加入runloop的操作中,timer被强引用。而timer作为局部变量,是无法执行invalidate的,所以在timer被invalidate之前,self也就不会被释放。
NSTimer *timer = [NSTimer timerWithTimeInterval:10 target:self selector:@selector(commentAnimation) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
解决方法:
主动stoptimer,至少是不能在dealloc中stoptimer的。另外可以设置一个中间类,把target变成中间类。
4 NSURLSession
NSURLSession *section = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
delegate:self
delegateQueue:[[NSOperationQueue alloc] init]];
NSURLSessionDataTask *task = [section dataTaskWithURL:[NSURL URLWithString:path]
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
//Do something
}];
[task resume];
和NSTimer问题类似,这里NSURLSession会强引用了self。同时本地SSL会对一个NSURLSession缓存一段时间。所以即使没有强引用。也会造成内存泄漏。这里比较好的使用单例[NSURLSession sharedSession]
5 非OC对象的内存问题
在OC对象转换为非OC对象时候,要进行桥接。要把对象的控制权由ARC转换为程序员自己控制,这时候程序员要自己控制对象创建和释放。如下面的简单代码
NSString *name = @"boyce";
CFStringRef cfStringRef = (__bridge CFStringRef) name;
CFRelease(cfStringRef);
6 其他泄漏情况
如果present一个UINavigationController,如果返回的姿势不正确。会造成内存泄漏
UIViewController *vc = [[UIViewController alloc]init];
UINavigationController *nav = [[UINavigationController alloc]initWithRootViewController:vc];
[self presentViewController:nav animated:YES completion:NULL];
如果在UIViewController里边调用的是如下代码,那么就会造成内存泄漏,这里边测试发现vc是没有被释放的。
[self dismissViewControllerAnimated:YES completion:NULL];
解决方法:
if (self.navigationController.topViewController == self) {
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
}
7 ARC内存泄露的检测
7.1 使用Xcode自带工具Instrument
7.2 在对象dealloc中进行打印
我们生成的对象,在即将释放的时候,会调用dealloc方法。所以我们可以在dealloc打印当前对象已经释放的消息。如果没有释放,对象的dealloc方法就永远不会执行,此时我们知道发生了内存泄露。