常见的容易造成泄漏的点

1 循环引用

081.png
082.png
083.png
084.png
085.png
086.png
087.png
088.png

不建议使用:


089.png
090.png
091.png

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
image.png
7.2 在对象dealloc中进行打印

我们生成的对象,在即将释放的时候,会调用dealloc方法。所以我们可以在dealloc打印当前对象已经释放的消息。如果没有释放,对象的dealloc方法就永远不会执行,此时我们知道发生了内存泄露。

你可能感兴趣的:(常见的容易造成泄漏的点)