Search code examples
objective-cxcodeallocation

What causes a "Virtual Memory exhausted" error?


Alright.. Here's the thing.. I am building an app in which when the user taps on download button it downloads a bunch on images (296, to be exact).

In simulator everything works flawless, on my iPhone (4S) on around 100th image it crashes with error:

malloc: * mach_vm_map(size= "some random number") failed (error code= 3)* error: can't allocate region

libBacktraceRecording.dylib: allocate_free_list_pages() -- virtual memory exhausted!

Here's the code I wrote for downloading those images:

-(void)getData
{
    NSError *error;
    int i;
    NSArray *brojLinije = [[NSArray alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"linije" ofType:@"plist"]];
    NSArray *urlSlike = [[NSArray alloc] initWithContentsOfFile:[[NSBundle mainBundle]pathForResource:@"urlSlike" ofType:@"plist"]];
    NSArray *pocetno = [[NSArray alloc] initWithContentsOfFile:[[NSBundle mainBundle]pathForResource:@"pocetno" ofType:@"plist"]];
    NSArray *sortiranje = [[NSArray alloc] initWithContentsOfFile:[[NSBundle mainBundle]pathForResource:@"sort" ofType:@"plist"]];
    NSArray*paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *desktopDir = [paths firstObject];
    UIImage *image1 = [[UIImage alloc] init];

    for (i = 0; i<296; i++) {

        NSString *brojLinije1 = [NSString stringWithFormat:@"%@",[brojLinije objectAtIndex:i]];
        NSString *pocetno1 = [NSString stringWithFormat: @"%@", [pocetno objectAtIndex:i]];
        NSString *tableSort = [NSString stringWithFormat: @"%@", [sortiranje objectAtIndex:i]];
        image1 = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat: @"http://www.busevi.com/images/stories/Red-Voznje/Gradski-Prevoz-BG/linija.%@.png", [urlSlike objectAtIndex:i ] ]]]];
        NSData *data1 = [NSData dataWithData:UIImageJPEGRepresentation(image1, 0.1)];

        NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
        NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];
        NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];

        NSString *pngFilePath = [NSString stringWithFormat:@"%@%@.jpg",desktopDir,[urlSlike objectAtIndex:i]];
        [data1 writeToFile:pngFilePath atomically:YES];

        [newManagedObject setValue:brojLinije1 forKey:@"brojLinije"];
        [newManagedObject setValue:data1 forKey:@"imageData"];
        [newManagedObject setValue:pocetno1 forKey:@"pocetnoStajaliste"];
        [newManagedObject setValue:tableSort forKey:@"sort"];
        NSLog(@"%d / 296", i);
    }

    [self.managedObjectContext save:&error];
}

Only thing I know is that the image allocation frequency (too much allocating and no time to automatically release) is making the problem, AND that I have tried every method I know so far, AND I HAVE watched A LOT of "Instruments" app tutorials and only one helped (to find the source of filling virtual memory) but I still can't solve my problem.


Solution

  • While using SDWebImage framework I encountered a lot of bugs and unanswered crashes from the app. SDWebImage is awesome if the count of images that are about to be downloaded is lower that, let's say 50. Because, when downloading one by one image that are showed in TableViewCell + fast scrolling (fast flicking), the app receives memory warning a couple of times while the TableView is still scrolling (SDWebImage will clear memory ONLY when TableView is slowly scrolling or not scrolling at all) so it crashes the app.

    So my only solace with app crashing while downloading 300+ images was simple Apple's method called dispatch_async. More about it here.

    And this is how I solved my problem (I just had to move everything from my original -(void)getdata method to dispatch_async block. DONE! So now it looks like this:

    -(void)getData {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSLog(@"Downloading Started");
            slike = [[NSArray alloc]initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"slike" ofType: @"plist"]];
            brojevi = [[NSArray alloc]initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"linije" ofType:@"plist"]];
            pocetnaStajalista = [[NSArray alloc]initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"pocetno" ofType:@"plist"]];
            sort = [[NSArray alloc]initWithContentsOfFile:[[NSBundle mainBundle]pathForResource:@"sort" ofType:@"plist"]];
    
            NSEntityDescription *entity = [NSEntityDescription entityForName:@"Linija" inManagedObjectContext:self.managedObjectContext];
            NSString *brojLinije;
            NSString *pocetnoStajaliste;
            NSString *sortiranje;
            NSData *data;
            NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
            NSString  *documentsDirectory = [paths objectAtIndex:0];
    
            NSString  *filePath = [NSString stringWithFormat:@"%@/%@", documentsDirectory,@"filename.png"];
    
            for (int i = 0; i<314; i++) {
                NSManagedObject *novaLinija = [[NSManagedObject alloc]initWithEntity:entity insertIntoManagedObjectContext:self.managedObjectContext];
    
                brojLinije = [NSString stringWithFormat:@"%@",brojevi[i]];
                pocetnoStajaliste = [NSString stringWithFormat:@"%@",pocetnaStajalista[i]];
                data = [NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat: @"http://www.busevi.com/images/stories/Red-Voznje/Gradski-Prevoz-BG/linija.%@.png",slike[i]]]];
                [data writeToFile:filePath atomically:YES];
                sortiranje = [NSString stringWithFormat: @"%@", sort[i]];
    
                [novaLinija setValue:data forKey:@"slikaLinije" ];
                [novaLinija setValue:brojLinije forKey:@"brojLinije"];
                [novaLinija setValue:pocetnoStajaliste forKey:@"pocetnoStajaliste"];
                [novaLinija setValue:sortiranje forKey: @"sort"];
                NSLog(@"%d / 314", i+1);
                NSError *error;
                [self.managedObjectContext save:&error];
            } }
    

    Hope this helps others with same problem. Good Luck and Happy Coding!