My app crashes when downloading large pdfs (>2MB)

2019-07-29 08:34发布

问题:

My app is working fine for downloading small Pdf's and showing them in WebView but when i fetch url of pdfs having size more than 2 MB it crashes everytime.

Code :-

@implementation SecondViewController

@synthesize scrollView,receivedData,myIndicator;

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data

{   
    [receivedData appendData:data]; 
}

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
    [super viewDidLoad];

    [myIndicator setActivityIndicatorViewStyle:UIActivityIndicatorViewStyleWhiteLarge];
    myIndicator.hidesWhenStopped = YES;
    [myIndicator startAnimating];

    UIColor *background = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:@"iphone_landscape.png"]];
    self.view.backgroundColor = background;
    [background release];   

    NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://litofinter.es.milfoil.arvixe.com/displayxml1.aspx"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:150.0];

    NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];

    if (theConnection) {
        receivedData = [[NSMutableData data] retain];
    }

}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    int x=10,y=50;

    appDelegate = (AppDelegate_iPhone *)[[UIApplication sharedApplication] delegate];

    scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 45,320, 480)];   
    scrollView.contentSize = CGSizeMake(320,5000);
    scrollView.showsVerticalScrollIndicator = YES;

    for (Litofinter *lito in appDelegate.bookArray) {
        if([appDelegate.currentButtonPressed isEqualToString:lito.cName])
        {
            NSLog(@"Count == %d ===",[lito.productsArray count]);
            for (Products *prod in lito.productsArray) {

                NSString * urlString = [prod.thumbnail stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
                NSURL * imageURL = [NSURL URLWithString:urlString];

                NSData * imageData = [NSData dataWithContentsOfURL:imageURL];
                UIImage * image = [UIImage imageWithData:imageData];

                [myIndicator stopAnimating];
                [myIndicator removeFromSuperview];

                UIButton *imageButton = [[UIButton buttonWithType:UIButtonTypeCustom]retain];
                [imageButton setFrame:CGRectMake(x, y, 140, 190)];
                [imageButton setImage:image forState:UIControlStateNormal];
                [imageButton setTitle:prod.pdf forState:UIControlStateNormal];
                [imageButton addTarget:self action:@selector(onTapBook:) forControlEvents:UIControlEventTouchUpInside];

                [scrollView addSubview:imageButton];

                x = x + 160;

                if(x >300)
                {
                    y = y +250;
                    x = 10;

                }
            }
        }
    }
    [self.view addSubview:scrollView];

    [connection release];
    [receivedData release];


}

-(void)onTapBook:(id)sender{

    UIButton *button = (UIButton *) sender;
    appDelegate.currentBookPressed = [button currentTitle];
    UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"Ver Catalogo!" message:@"" delegate:self cancelButtonTitle:@"Cancelar" otherButtonTitles:@"Ver on-line",@"Descargar",nil];
    [alert show];
}

// when I click on Descargar button i.e. download it goes to next PdfShowViewController

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex  
{  

    NSString *title = [alertView buttonTitleAtIndex:buttonIndex];  

    if([title isEqualToString:@"Ver on-line"])  
    { 
        viewController2 = [[PdfShowViewController alloc]initWithNibName:@"PdfShowViewController" bundle:nil];
        [self presentModalViewController:viewController2 animated:YES];

    }  

    else if([title isEqualToString:@"Descargar"])  
    {       
        viewController2 = [[PdfShowViewController alloc]initWithNibName:@"PdfShowViewController" bundle:nil];
        [self presentModalViewController:viewController2 animated:YES];
    }  

} 


-(IBAction)onTapBack{
    [self dismissModalViewControllerAnimated:YES];
}


- (void)dealloc {
    [super dealloc];
    [scrollView release];
}


@end

// here i am showing the pdf on a webView and downloading them

@implementation PdfShowViewController

@synthesize pdfWebview,myIndicator,progress,receivedData,DownloadRequest,DownloadConnection,downloadLabel,openURL;

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [receivedData appendData:data];

    unsigned char byteBuffer[[receivedData length]];
    [receivedData getBytes:byteBuffer];
    NSLog(@"Data === %ld",receivedData);

    NSInteger receivedLen = [data length];
    bytesReceived = (bytesReceived + receivedLen);
    NSLog(@"received Bytes ==  %f",bytesReceived);

    if(expectedBytes != NSURLResponseUnknownLength) 
    {
        NSLog(@"Expected Bytes in if ==  %f",expectedBytes);
        NSLog(@"received Bytes in if ==  %f",bytesReceived);

        float value = ((float) (bytesReceived *100/expectedBytes))/100;
        NSLog(@"Value ==  %f",value);
        progress.progress=value;
    }

}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    [connection release];
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    expectedBytes = [response expectedContentLength];
    NSLog(@"%f",expectedBytes);

}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {

    [myIndicator stopAnimating];
    [myIndicator removeFromSuperview];
    [progress setHidden:YES];
    [downloadLabel setHidden:YES];

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *pdfPath = [documentsDirectory stringByAppendingPathComponent:@"MayankPdf.pdf"];

    unsigned char byteBuffer[[receivedData length]];
    [receivedData getBytes:byteBuffer];

    [self.receivedData  writeToFile:pdfPath atomically:YES];

    [connection release];

    //Now create Request for the file that was saved in your documents folder

    NSURL *url = [NSURL fileURLWithPath:pdfPath];

    NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];

    [pdfWebview setScalesPageToFit:YES];
    [pdfWebview loadRequest:requestObj];
}

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
    [super viewDidLoad];

    appDelegate = (AppDelegate_iPhone *)[[UIApplication sharedApplication] delegate];

    [downloadLabel setText:@"Downloading..."];
    [downloadLabel setHidden:NO];

    [myIndicator setActivityIndicatorViewStyle:UIActivityIndicatorViewStyleWhiteLarge];
    myIndicator.hidesWhenStopped = YES;
    [myIndicator startAnimating];

    NSLog(@"REquired --------------------------------%@",appDelegate.currentBookPressed);
    //  NSString *urlString = [appDelegate.currentBookPressed stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
    NSString *urlString = [appDelegate.currentBookPressed stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

    NSLog(@"The Url Stirng=======%@",urlString);

    NSURL *targetURL = [NSURL URLWithString:urlString];
    NSLog(@"Trageted String ------======++++++++%@",targetURL);
    DownloadRequest = [NSURLRequest requestWithURL:targetURL cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:120.0];
    DownloadConnection = [[NSURLConnection alloc] initWithRequest:DownloadRequest delegate:self];

    if (DownloadConnection) {
        receivedData = [[NSMutableData data]retain];
    }

    [pdfWebview setScalesPageToFit:YES];
    //  [pdfWebview loadRequest:DownloadRequest];


}

-(IBAction)onTapBack
{
    [self dismissModalViewControllerAnimated:YES];
}

- (void)dealloc {
    [super dealloc];
    [pdfWebview release];
    //  [receivedData release];
}


@end

My parser also not accepting URLS having space, any solutions will be appreciated. Thanks in Advance

Pardon me if have written something wrong as i am not very good in Iphone.

回答1:

you are probably exceeding the 30MB RAM limit,

you shouldn't use a NSMutableData object to reconstruct the full file but write it directly on "disk" to limit memory usage.

to inspire you

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {

    @synchronized(connection) {
        m_downloadedBytes += [data length];
        CGFloat downloadProgress = ((CGFloat) m_downloadedBytes / (CGFloat) m_fileSize);
        [self setDownloadProgress:downloadProgress];        

        if (!m_fileHandle) {
            [data writeToURL:m_tmpTargetURL atomically:NO];
            m_fileHandle = [[NSFileHandle fileHandleForWritingToURL:m_tmpTargetURL error:NULL] retain];
            [m_fileHandle seekToEndOfFile];
        }
        else {
            [m_fileHandle writeData:data];

        }
    }
}


回答2:

From what you say in your comment (app crashing on the device but not on the simulator) and taking into account that you are downloading large PDF files, my guess is that you are running into memory problems and your app is killed by the system (this is usually associated with a log message about a signal 0 being sent to the app).

If this is true, I would suggest that you store the data you download incrementally; i.e., you don't append all the data in NSData and at the very end (when you got all data) you store them to a file. You can append to a file each received chunk and free your memory.

On the other hand, I don't understand very well why you are doing this:

unsigned char byteBuffer[[receivedData length]];
[receivedData getBytes:byteBuffer];

since you are not using byteBuffer elsewhere in the connectionDidFinishLoading method. This is not the cause of the issue, I think, since the local variable is deallocated thereafter, but still if you receive a big chunk of data you are duplicating it (once in NSData, a second time on the stack).

About the space problem in your URLs, you should think of escaping them by using

stringByAddingPercentEscapesUsingEncoding:

which is a NSString method.



回答3:

First most of thing you alloc is not released(theconnection,alert,viewcontroller2)

second [connection release]; in - (void)connectionDidFinishLoading:(NSURLConnection *)connection which connection you are releasing

after clearing this issue check and run the app. then explain what is your exception or error.