Search code examples
iosmemorymemory-leaksinstruments

Memory leaks by click screen, network many times, lead to crash in real device


As the users test my app, he random click the screen very quickly, like a monkey , to into my every detail view controller, because every into my detail vc, the vc will fetch data from the net. And in the real device my app crash. I think this issue is cause by memory leak, because this did not appear in simulator, I test my app in instrument, the screenshoot in below:

In instrument:

The situation:

  1. my main screen, click the every item on the main screen will in to a different detail vc.

  2. detail one vc, every in detail vc, user test will fetch data or click random:

  3. the memory grow to 90.5MB, and did not come down, if use user's test method, if take more long time, will be more than 90.5MB:

I use instrument is not that well, and the memory is most used by AFNetworking, i don't know how to do with this. Someone can give advice? Most thanks in advance.

EDIT

I make the AFHTTPSessionManager to be a singleton, but I found my net request become slowly, does this affect me?

#import "Mysevers.h"
#import "AFNetworking.h"
#import "HUD.h"


static AFHTTPSessionManager *requestManager ;

@implementation Mysevers

+ (AFHTTPSessionManager *)sharedHTTPSession{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        requestManager = [AFHTTPSessionManager manager];
        requestManager.requestSerializer.timeoutInterval = 10;
    });
    return requestManager;
}


+(void)AFPOSTWithHud:(BOOL)hud andAddressname:(NSString*)addressName parmas:(NSDictionary*)parmas RequestSuccess:(void(^)(id result))success failBlcok:(void(^)(void))failBlcok
{

    if (hud) {
        //[HUD addHUD];
    
        [SVProgressHUD show];
    }

    AFHTTPSessionManager *requestManager = [Mysevers sharedHTTPSession];

    NSString *urlStr = [NSString stringWithFormat:@"%@%@",BASE_URL,addressName];
    DLog(@"%@",urlStr);

    [requestManager POST:urlStr parameters:parmas progress:^(NSProgress * _Nonnull uploadProgress) {
    
 } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        if (hud) {
            //[HUD removeHUD];
            [SVProgressHUD dismiss];
        }
        success(responseObject);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
    
        if (error != nil) {
            DLog(@"error==%@",[error localizedDescription]);
            if (hud) {
                //[HUD removeHUD];
                [SVProgressHUD dismiss];
            }
            failBlcok();
        }
    
    
        return ;
    }];


}


+(void)AFGETWithHud:(BOOL)hud andAddressname:(NSString*)addressName parmas:(NSDictionary*)parmas RequestSuccess:(void(^)(id result))success failBlcok:( void(^)(void))failBlcok
{    
    if (hud) {
        //[HUD addHUD];
        [SVProgressHUD show];
    }
    AFHTTPSessionManager *requestManager = [Mysevers sharedHTTPSession];

    NSString *urlStr = [NSString stringWithFormat:@"%@%@",BASE_URL,addressName];

    DLog(@"%@",urlStr);


    [requestManager GET:urlStr parameters:parmas progress:^(NSProgress * _Nonnull downloadProgress) {
    
} success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {

        if (hud) {

            //[HUD removeHUD];
            [SVProgressHUD dismiss];
        }
        success(responseObject);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        DLog(@"error==%@",[error localizedDescription]);
    
        if (hud) {
            //[HUD removeHUD];
            [SVProgressHUD dismiss];
        }
        failBlcok();
    }];
}


@end

Solution

  • You'll see this behavior if you are instantiating lots of new NSURLSession objects (i.e. which will happen if you instantiate a lot of AFHTTPSessionManager objects).

    For example, I wrote a simple program that instantiated 50 separate NSURLSession objects, did a simple data task for each (designated by a "sign post" under the "points of interest", below, all taking place during the region indicated by the green bar), and then immediately releasing them.

    You'll see that memory went up by almost 6mb, but after roughly 2 minutes (with absolutely no intervention on my part), it only started reclaiming that memory, returning more than half of the memory previously consumed by all of those session objects. This is a reproducible pattern that I see when issuing multiple session objects.

    enter image description here

    This can be solved by either calling finishTasksAndInvalidate on the NSURLSession objects, or, better, by making sure your app instantiates only one session object and then uses that same session object every time. Either one of these approaches will dramatically reduce the memory profile of the app (and reusing the same session object is simply more efficient).

    In short, I would ensure your detail view controller is not instantiating a new AFHTTPSessionManager object each time. Instantiate it once in your app and reuse it.

    Having said that, I wonder if you might also have some other memory problem as my 50 NSURLSession objects only consumed 6mb of memory, and it sounds like you're losing more than that. So, I'd suggest fixing the above, but if you're still seeing memory consumption going up (not necessary shown in Leaks anymore, though), perhaps you have some strong reference cycle or the like. The Xcode 8 "debug memory graph" tool (see How to debug memory leaks when Leaks instrument does not show them?) can be helpful in tracking those issues down.