Search code examples
objective-ciosphp-ziparchive

Memory warning in the iPad 1st generation and crash


i am developing an iPad application witch has a functionality downloading books. The books size are about 180 Mo. The books are in server and the have the extension .zip. I download the book (.zip) then i unzip it and then remove the .zip. I am doing like this :

- (BOOL)downloadBookWithRequest:(BookDownloadRequest*)book
{
    if (![book isValid])
    {
        NSLog(@"Couldn't launch download since request had missing parameter");
        return NO;
    }

    if ([self bookIsCurrrentlyDownloadingWithID:book.ID]) {
        NSLog(@"Book already downloaded");
        return NO;
    }

    ASIHTTPRequest *download = [[ASIHTTPRequest alloc] initWithURL:book.URL];

    download.userInfo = book.dictionary;
    download.downloadDestinationPath = [self downloadPathForBookID:book.ID];
    download.downloadProgressDelegate = self.downloadVC.downloadProgress;
    download.shouldContinueWhenAppEntersBackground = YES;

    [self.downloadQueue addOperation:download];

    [download release];

    // Update total requests
    self.requestsCount++;
    [self refreshDownloadsCount];

    if(self.downloadQueue.isSuspended)
        [self.downloadQueue go];

    [self.downloadVC show];

    return YES;
}



- (void)requestFinished:(ASIHTTPRequest*)request
{

    NSString *bookStoragePath = [[BooksManager booksStoragePath] stringByAppendingPathComponent:request.downloadDestinationPath.lastPathComponent.stringByDeletingPathExtension];
    NSString *bookZipPath = request.downloadDestinationPath;

    // Tell not to save the zip file into iCloud
    [BooksManager addSkipBackupAttributeToItemAtPath:bookZipPath];


    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSError *removeExistingError = nil;

    if ([fileManager fileExistsAtPath:bookStoragePath])
    {

        [fileManager removeItemAtPath:bookStoragePath error:&removeExistingError];

        if (removeExistingError)
        {
            [self bookDidFailWithRequest:request errorMessageType:DownloadErrorTypeFILE_ERROR];

            NSLog(@"ERROR: Couldn't remove existing book to unzip new download (%@)", removeExistingError);
        } else
            NSLog(@"INFO: Removed existing book to install new download");
    }

    ZipArchive* zip = [[ZipArchive alloc] init];

    if([self isCompatibleWithFileAtPath:bookZipPath] && [zip UnzipOpenFile:bookZipPath])
    {
        BOOL unzipSucceeded = [zip UnzipFileTo:bookStoragePath overWrite:YES];

        if (!unzipSucceeded)
        {
            [self bookDidFailWithRequest:request errorMessageType:DownloadErrorTypeFILE_ERROR];

            NSLog(@"ERROR: Couldn't unzip file %@\n to %@",bookZipPath,bookStoragePath);
        } else {
             [self bookDidInstallWithRequest:request];
            NSLog(@"INFO: Successfully unziped downloaded file");
        }
        [zip UnzipCloseFile];
    }
    else
    {
        [self bookDidFailWithRequest:request errorMessageType:DownloadErrorTypeFILE_ERROR];

        NSLog(@"ERROR: Unable to open zip file %@\n",bookZipPath);
    }

    [self removeZipFileAtPath:bookZipPath];
    [zip release];
}

-(BOOL) removeZipFileAtPath:(NSString*) bookZipPath {
    NSFileManager *fileManager = [NSFileManager defaultManager];
    if ([fileManager fileExistsAtPath:bookZipPath])
    {
        NSError *removeZipFileError = nil;

        [fileManager removeItemAtPath:bookZipPath error:&removeZipFileError];

        if (removeZipFileError) {
            NSLog(@"ERROR: Couldn't remove existing zip after unzip (%@)", removeZipFileError);
            return NO;
        }

        else {
            NSLog(@"INFO: Removed zip downloaded after unzip");
            return YES;
        }        
    }
    return NO;
}

My Problem is : This code is working fine with iPhone 4/iPhone 4s/ iPad 2G /iPad3G, but it crash with an iPad 1st Generation (when unzipping the book) and the crash reporter says that the are Memory warning.

How question is, how i can optimize this code to avoid the memory warning and avoid the crash ? Thanks for your answers;

Edit : I have found that the problem is caused by this portion of code :

NSData *bookData = [[NSData alloc]initWithContentsOfFile:bookPath];

the bookPath is the path to the .zip ( about 180 Mo) and when i am in iPad 1G this line crash my application i.e. : i receive memory warnings and the system kill the App. Du you know how i can avoid this. I an using this line to calculate the MD5 of the book (.zip)

I have a category in NSData like this :

#import <CommonCrypto/CommonDigest.h>

@implementation NSData(MD5)

- (NSString*)MD5
{
    // Create byte array of unsigned chars
  unsigned char md5Buffer[CC_MD5_DIGEST_LENGTH];

    // Create 16 byte MD5 hash value, store in buffer
  CC_MD5(self.bytes, self.length, md5Buffer);

    // Convert unsigned char buffer to NSString of hex values
  NSMutableString *output = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
  for(int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) 
        [output appendFormat:@"%02x",md5Buffer[i]];

  return output;
}

How i can avoid the crash ? thanks


Solution

  • EDIT:

    So, it seems that the culprit is loading into memory the whole file in order to calculate its MD5 hash.

    The solution to this would be calculating the MD5 without having to load into memory the whole file. You can give a look at this post explaining how to compute efficiently an MD5 or SHA1 hash, with the relative code. Or if you prefer, you can go directly to github and grab the code.

    Hope it helps.

    OLD ANSWER:

    You should inspect your app, especially the ZipArchive class, for memory leaks or not-released memory. You can use Instruments' Leaks and Memory Allocation tools to profile your app.

    The explanation of the different behavior between iPad1 and the rest of devices may lay with their different memory footprint, as well as with different memory occupation states of the devices (say, you iPad 1 has less free memory when you run the app then the iPad 2 because of the state other apps you ran on the iPad 1 left the device in). You might think of rebooting the iPad 1 to inspect its behavior out of a fresh start.

    In any case, besides the possible explanation of the different behaviors, the ultimate cause is how your app manages memory and Instruments is the way to go.