iOS NSURLConnection memory leak - iphone

i got code like this
#import "UIWebImageView.h"
#interface UIWebImageView (hiddenMethods)
- (void) initDefaults;
#end
#implementation UIWebImageView (hiddenMethods)
- (void) initDefaults
{
self.showActivityIndicator = NO;
self.activityIndicatorStyle = UIActivityIndicatorViewStyleWhite;
self.activityIndicatorSize = CGSizeMake(20.0, 20.0);
//data = [[NSMutableData alloc] init];
}
#end
#implementation UIWebImageView
#synthesize showActivityIndicator;
#synthesize activityIndicatorStyle;
#synthesize activityIndicatorSize;
- (id) init
{
if (self = [super init])
{
[self initDefaults];
}
return self;
}
- (void) loadFromURL:(NSString *) url
{
[self.image release];
self.image = nil;
request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]
cachePolicy:NSURLRequestReturnCacheDataElseLoad
timeoutInterval:30.0];
if (connection == nil)
connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
// activity indicator start
indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:activityIndicatorStyle];
indicator.frame = CGRectMake((self.frame.size.width - activityIndicatorSize.width)/2, (self.frame.size.height - activityIndicatorSize.height)/2,
activityIndicatorSize.width, activityIndicatorSize.height);
[self addSubview:indicator];
[indicator startAnimating];
}
- (void) dealloc
{
[data release];
[indicator release];
[connection release];
[super dealloc];
}
#pragma mark -
#pragma mark connection delegate
- (void) connection:(NSURLConnection *)theConnection didReceiveData:(NSData *)incrementalData
{
if (data == nil)
data = [[NSMutableData alloc] initWithCapacity:2048];
[data appendData:incrementalData];
}
- (void) connectionDidFinishLoading:(NSURLConnection *)theConnection
{
self.image = [UIImage imageWithData:data];
[indicator removeFromSuperview];
//data = nil;
[data release], data = nil;
[connection release], connection = nil;
[indicator release], indicator = nil;
}
- (void) connection:(NSURLConnection *)theConnection didFailWithError:(NSError *)error
{
self.image = [UIImage imageNamed:#"icon.jpg"];
[indicator removeFromSuperview];
//data = nil;
[data release], data = nil;
[connection release], connection = nil;
[indicator release], indicator = nil;
}
I'm using this for downloading images from web. one image for every cell in in table. and it works fine when image is not going out of the screen. BUT when u scrolling tableView fast and some images did not finished loading while they are on screen there huge memory leaks.
i know where is the leak and why its leaking. but i can't find solution.
any thoughts ?
thank you
PS sorry for my english
UPDATE
here is a code for adding images into tableView
UIWebImageView *tmpImageView = [[UIWebImageView alloc] initWithFrame:CGRectMake(0, 0, 57, 76)];
tmpImageView.showActivityIndicator = YES;
tmpImageView.contentMode = UIViewContentModeScaleAspectFit;
tmpImageView.activityIndicatorStyle = UIActivityIndicatorViewStyleGray;
[tmpImageView loadFromURL:[[tableArray objectAtIndex:indexPath.row] objectForKey:#"picurl"]];
[cell addSubview:tmpImageView];
[tmpImageView release];
and I'm repeating = ) its leaking only when it not finished loading and went off the screen while scrolling

The leaks are happening due to the allocation of UIWebImageView objects recursively (considering you are using reusable cells).
you should change your code to:
UIWebImageView *tmpImageView = [cell viewWithTag:2011];
if(!tmpImageView)
{
tmpImageView = [[UIWebImageView alloc] initWithFrame:CGRectMake(0, 0, 57, 76)];
tmpImageView.showActivityIndicator = YES;
tmpImageView.tag = 2011;
tmpImageView.contentMode = UIViewContentModeScaleAspectFit;
tmpImageView.activityIndicatorStyle = UIActivityIndicatorViewStyleGray;
[cell addSubview:tmpImageView];
[tmpImageView release];
}
[tmpImageView loadFromURL:[[tableArray objectAtIndex:indexPath.row] objectForKey:#"picurl"]];
you have to handle for the showActivityIndicator thing some how...based upon your code but the above mentioned change will remove your memory leaks.

Autorelase your NSURLConnection with this syntax
connection = [NSURLConnection connectionWithRequest:request delegate:self];
and delete your [connection release], connection = nil;

Related

Webview content is not readable after stop animating loading image

I am building one iPhone application where I have two views one for login view and another for web view. When user enters the login credentials, then it will redirect to web view. while loading the webview from login view too much time is taking to load. hence I used alertview to show loading image.
#interface FocusViewController ()
#end
#implementation FocusViewController
#synthesize txtsecurecode;
#synthesize webView;
#synthesize OrganizationCode;
UIActivityIndicatorView *indicator;
UIAlertView *alert;
- (void)viewDidLoad
{
[super viewDidLoad];
appDelegate = (FocusAppDelegate *)[[UIApplication sharedApplication]delegate];
if(appDelegate.data.strOrganizationCode == nil || appDelegate.data.strAccessCode == nil)
{
NSLog(#"YOUR FIRST SCREEN");
_loginView.hidden = NO;
webView.hidden = YES;
}
else
{
NSLog(#"LOAD WEBVIEW DIRECTLY\n");
NSLog(#"Organization Code -> %# \n Secure Access Code -> %#",appDelegate.data.strOrganizationCode,appDelegate.data.strAccessCode);
OrganizationCode.text = appDelegate.data.strOrganizationCode ;
txtsecurecode.text = appDelegate.data.strAccessCode;
[self loginClicked:nil];
webView.hidden = NO;
_loginView.hidden = YES;
}
}
- (IBAction)loginClicked:(id)sender {
#try {
if([[txtsecurecode text] isEqualToString:#""] || [[OrganizationCode text] isEqualToString:#""] ) {
[self alertStatus:#"Please enter Access code" :#"Login Failed!":0];
}
else
{
NSString *post =[[NSString alloc] initWithFormat:#"txtsecurecode=%# #&password=%#",[txtsecurecode text],[OrganizationCode text]];
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"http://myexample.com/AccountService/security/ValidateAccess?accesscode=%#&companycode=%#&type=1", txtsecurecode.text, OrganizationCode.text]];
NSString *responseData = [[NSString alloc]initWithData:[NSData dataWithContentsOfURL:url] encoding:NSUTF8StringEncoding];
if([responseData isEqualToString:#""]){
[self alertStatus:#"Please enter valid Access Code" :#"Login Failed !" :0];
}
else
{
appDelegate.data.strOrganizationCode = OrganizationCode.text;
appDelegate.data.strAccessCode = txtsecurecode.text;
[FocusAppDelegate addCustomObjectToUserDefaults:appDelegate.data key:kCredentails];
//Updated
_loginView.hidden = YES;
webView.hidden = NO;
responseData = [responseData stringByReplacingOccurrencesOfString:#" "" " withString:#""];
NSString* encodedString = [responseData stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSLog(#"Response ==> %#" ,encodedString);
alert = [[[UIAlertView alloc] initWithTitle:#"Loading\nPlease Wait..." message:nil delegate:self cancelButtonTitle:nil otherButtonTitles: nil] autorelease];
[alert show];
UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
indicator.center = CGPointMake(alert.bounds.size.width / 2, alert.bounds.size.height - 50);
[indicator startAnimating];
[alert addSubview:indicator];
[indicator release];
webView.backgroundColor = [UIColor clearColor];
webView.opaque = NO;
webView.delegate = self;
webView.frame = self.view.bounds;
NSString* urlTwo = [[encodedString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]
stringByReplacingOccurrencesOfString:#"%22" withString:#""];
NSURL *url2;
if([urlTwo hasPrefix:#"http://"]){
url2 = [NSURL URLWithString:urlTwo];
}else{
url2 = [NSURL URLWithString:[NSString stringWithFormat:#"http://%#" , urlTwo]];
}
NSLog(#"url2:%#", url2);
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url2];
[webView loadRequest:requestObj];
[[self view] addSubview:webView];
}
}
}
#catch (NSException * e) {
NSLog(#"Exception: %#", e);
[self alertStatus:#"Login Failed." :#"Login Failed!" :0];
}
}
-(void)webViewDidStartLoad:(UIWebView *)webView{
NSLog(#"webViewDidStartLoad");
[self.view addSubview:self.indicator];
alert.hidden = NO;
}
- (void) webViewDidFinishLoad:(UIWebView *)webView {
NSLog(#"webViewDidFinishLoad");
[self.indicator removeFromSuperview];
[self.alert dismissWithClickedButtonIndex:1 animated:NO] ;
}
- (IBAction)backgroundClick:(id)sender
{
[txtsecurecode resignFirstResponder];
[OrganizationCode resignFirstResponder];
}
#end
Until content in webview displays, loading image is displaying and working good. but content of webview is not editable and remaining static as I think I used 'web view.hidden = YES'. when I comment the alert method and run then content of webview is also editable and working good as required. I need to load the content of url after loading image stops loading.
I would suggest show the activity indicator from the Viewcontroller having the WebView and implement the WebView Delegate methods
- (void)webViewDidStartLoad:(UIWebView *)webView
{
[actvityIndicator startAnimating];
}
and
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
[actvityIndicator stopAnimating];
}
this will keep your webview Interactive and also show the indicator till the page loads completely
you can try something like this. The Delegate is important in .h file
FocusViewController.h
#interface FocusViewController : UIViewController <UIWebViewDelegate> {
UIActivityIndicatorView *indicator;
}
FocusViewController.m
- (void)viewDidLoad
{
// Loading Indicator
indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
[indicator setColor:[UIColor colorWithRed:50.0/255.0 green:120.0/255.0 blue:200.0/255.0 alpha:1.0]];
}
- (void)webViewDidStartLoad:(UIWebView *)webView
{
[indicator startAnimating];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
[indicator stopAnimating];
}

Can i load images from the device's documents directory to AsyncImageView?

Actually, I am not sure about this but i have this condition where i check whether internet is available or not and accordingly load the image into AsyncImageView. It works fine when i load any image from the internet, but is it possible to load a local image to AsyncImageView?
I guess you mean AsyncImageView by Nick Lockwood? This is simply a category to UIImageView, so when you have the image already, why not set the image property directly?
Actually i got the solution to this problem.All i did was this
NSURL *theURL = [NSURL fileURLWithPath:#"/Users/gaurav/Library/Application Support/iPhone Simulator/5.0/Applications/AC6E9B2E-DB25-4933-A338-755B134E1A61/Documents/Attachment290_294_1344928691.jpeg"];
NSLog(#"local URl is::%#",theURL);
[self.asyncimageview loadImageFromURL:theURL];
Perhaps the following will help you.
Make a new file
Inherit from UIImageView
Copy/Paste the following code.
import
import "ASIHTTPRequest.h"
#interface AsynchronousImageView : UIImageView{
NSMutableData *data;
UIActivityIndicatorView *activityIndicator;
NSString *urlString; // key for image cache dictionary
ASIHTTPRequest *requestLocal;
}
#property(nonatomic,assign) UIActivityIndicatorView *activityIndicator;
(void)loadImageFromURLString:(NSString *)theUrlString;
(void)loadImageFromNSURL:(NSURL *)theUrl;
(void)loadImageFromFile:(NSString *)filePath;
-(void) showLoader;
-(void) stopLoader;
#end
Then In .m file paste the following.
import "AsynchronousImageView.h"
import "ASIDownloadCache.h"
#implementation AsynchronousImageView
#synthesize activityIndicator;
(id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
//[self setContentMode:UIViewContentModeScaleAspectFit];
}
return self;
}
-(void)dealloc
{
if (requestLocal) {
[requestLocal setDelegate:nil];
}
requestLocal = nil;
activityIndicator = nil;
[super dealloc];
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
(void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
-(void) showLoader{
if(activityIndicator == nil){
activityIndicator = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
[self addSubview:activityIndicator];
activityIndicator.frame = CGRectMake(self.bounds.size.width / 2.0f - activityIndicator.frame.size.width /2.0f, self.bounds.size.height / 2.0f - activityIndicator.frame.size.height /2.0f, activityIndicator.frame.size.width, activityIndicator.frame.size.height);
[activityIndicator release];
}
[activityIndicator startAnimating];
}
-(void) stopLoader{
if(activityIndicator){
[activityIndicator stopAnimating];
[activityIndicator removeFromSuperview];
activityIndicator = nil;
}
}
(void)loadImageFromFile:(NSString *)filePath {
UIImage *img = [UIImage imageWithContentsOfFile:filePath];
[self setImage:img];
[self stopLoader];
}
(void)loadImageFromURLString:(NSString *)theUrlString
{
/if (requestLocal) {
[requestLocal setDelegate:nil];
}/
requestLocal = nil;
[self showLoader];
NSURL *url = [NSURL URLWithString:theUrlString];
requestLocal = [ASIHTTPRequest requestWithURL:url];
[requestLocal setDelegate:self];
[requestLocal setDidFailSelector:#selector(requestFailed)];
[requestLocal setDownloadCache:[ASIDownloadCache sharedCache]];
[requestLocal setCacheStoragePolicy:ASICachePermanentlyCacheStoragePolicy];
[requestLocal startAsynchronous];
self.image = [UIImage imageNamed:#"playerDefault.png"];
}
-(void)loadImageFromNSURL:(NSURL *)theUrl
{
if (requestLocal) {
[requestLocal setDelegate:nil];
}
requestLocal = nil;
requestLocal = [ASIHTTPRequest requestWithURL:theUrl];
[requestLocal setDelegate:self];
[requestLocal setDidFailSelector:#selector(requestFailed)];
[requestLocal startAsynchronous];
}
(void)requestFinished:(ASIHTTPRequest *)request
{
requestLocal = nil;
[self stopLoader];
UIImage *tempArt = [UIImage imageWithData:[request responseData]];
if (tempArt) {
self.image = tempArt;
}
[data release];
data = nil;
}
-(void)requestFailed:(ASIHTTPRequest *)request{
requestLocal = nil;
[self stopLoader];
[data release];
data = nil;
}
#end
Then in your ViewComtroller .h import AsycnhronousImageView class and write this
Make the IBOutlet of your image like this.
IBOutlet AsycnhronousImageView *image;
Change the class in xib AsycnhronousImageView rather than UIImage View
Call the following Method
(void)loadImageFromFile:(NSString *)filePath;

i want to save my pdf into my iphone, pdfs url are with me through parsing

I have parsed my xml and i got some images and their corresponding urls of pdf from server.so whenever i click on image i have their corresponding url of pdf.I am giving an alertView on click of images and when user select the download button of alertView it should download the pdf from url into my iphone device
CODE:-
#implementation SecondViewController
#synthesize scrollView,receivedData;
- (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=20,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, 150, 200)];
[imageButton setImage:image forState:UIControlStateNormal];
[imageButton setTitle:prod.pdf forState:UIControlStateNormal];
[imageButton addTarget:self action:#selector(onTapBook:) forControlEvents:UIControlEventTouchUpInside];
[scrollView addSubview:imageButton];
x = x + 150;
if(x >300)
{
y = y +250;
x = 20;
}
}
}
}
[self.view addSubview:scrollView];
[connection release];
[receivedData release];
}
-(void)onTapBook:(id)sender{
UIButton *button = (UIButton *) sender;
appDelegate.currentBookPressed = [button currentTitle];
// viewController2 = [[PdfShowViewController alloc]initWithNibName:#"PdfShowViewController" bundle:nil];
// [self presentModalViewController:viewController2 animated:YES];
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Ver Catalogo!" message:#"" delegate:self cancelButtonTitle:#"Cancelar" otherButtonTitles:#"Ver on-line",#"Descargar",nil];
[alert show];
/*[NSString stringWithFormat:#"%#",appDelegate.currentBookPressed] */
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSString *title = [alertView buttonTitleAtIndex:buttonIndex];
if([title isEqualToString:#"Ver on-line"])
{
// i will show the pdf online here
}
else if([title isEqualToString:#"Descargar"])
{
// what to write to download the pdf
}
}
-(IBAction)onTapBack{
[self dismissModalViewControllerAnimated:YES];
}
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload {
[super viewDidUnload];
}
- (void)dealloc {
[super dealloc];
[scrollView release];
}
#end
I would do it with NSURLConnection and then I would reuse same code above, because you have it already declared properly.
Save data to NSData and then with writeToFile save it to main bundle.
So here is some more explanation how I would do it.
There are several ways to do it.
Here is how to do it with NSData
NSData *myFile = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"your_url"]]; [myFile writeToFile:[NSString stringWithFormat:#"%#/%#", [[NSBundle mainBundle] resourcePath], #"yourfilename.pdf"] atomically:YES];
Also you can use ASIHTTPRequest library which has been discontinued by author, but still works like it should.
ASIHTTPRequest *myDownloadRequest = [ASIHTTPRequest requestWithURL:fileUrl];
[request setDownloadDestinationPath:[NSString stringWithFormat:#"%#/%#", [[NSBundle mainBundle] resourcePath], #"yourfilename.pdf"]];
But maybe easiest way of all because as I can see you have displayed pdf already, so it's contents are in receivedData is just to call
[receivedData writeToFile:[NSString stringWithFormat:#"%#/%#", [[NSBundle mainBundle] resourcePath], #"yourfilename.pdf"] atomically:YES];
So actually you can reuse code that you have already wrote in viewDidLoad, replace url if necessary and after connection is closed save file to disk.
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];
}

NSArray with multiple nested requests

I have an app that uses a segmentedControl. First item is an "All" item, where the rest is created from an array based on result from webservice. When "All" is selected I want to request all the request.
How can I go about this,
NSArray *urls = [NSArray arrayWithObjects:#"http://service/group/1/",
#"http://service/group/2/", nil];
I want to collect all result from the calls into a collection and display it in a UITableView when the "All" item is selected and probably in viewDidLoad.
For the other segments only one of the request is issued and callback with an array that then is used in:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
I have tried to look at this example for making the request from the array MultipleDownloads
Thanks,
The method in my viewController to initiate the multiple download:
- (void)requestChildrenInBackground {
queue = [[NSOperationQueue alloc] init];
//Todo remove hard coded and get from previous request respons
NSArray *urls = [NSArray arrayWithObjects: #"http://service/1/children",
#"http://service/2/children",
#"http://service/3/children", nil];
NSLog(#"%#", urls);
for (NSString * url in urls)
{
GetSchedule *operation =
[GetSchedule urlDownloaderWithUrlString:url];
[queue addOperation:operation];
}
}
This is how the multiple request gets handled:
#import "GetSchedule.h"
#import "JSON.h"
#import "Authentication.h"
#import "AttendanceReportViewController.h"
#interface GetSchedule ()
- (void)finish;
#end
#implementation GetSchedule
#synthesize appDelegate;
#synthesize username;
#synthesize password;
#synthesize authenticationString;
#synthesize encodedLoginData;
#synthesize schedulesArray;
#synthesize url = _url;
#synthesize statusCode = _statusCode;
#synthesize data = _data;
#synthesize error = _error;
#synthesize isExecuting = _isExecuting;
#synthesize isFinished = _isFinished;
+ (id)urlDownloaderWithUrlString:(NSString *)urlString {
NSURL * url = [NSURL URLWithString:urlString];
GetSchedule *operation = [[self alloc] initWithUrl:url];
return [operation autorelease];
}
- (id)initWithUrl:(NSURL *)url {
self = [super init];
if (self == nil)
return nil;
_url = [url copy];
_isExecuting = NO;
_isFinished = NO;
return self;
}
- (void)dealloc
{
[username release];
[password release];
[encodedLoginData release];
[_url release];
[_connection release];
[_data release];
[_error release];
[super dealloc];
}
- (BOOL)isConcurrent
{
return YES;
}
- (void)start
{
if (![NSThread isMainThread])
{
[self performSelectorOnMainThread:#selector(start) withObject:nil waitUntilDone:NO];
return;
}
self.username = appDelegate.username;
self.password = appDelegate.password;
Authentication *auth = [[Authentication alloc] init];
authenticationString = (NSMutableString*)[#"" stringByAppendingFormat:#"%#:%#", username, password];
self.encodedLoginData = [auth encodedAuthentication:authenticationString];
[auth release];
NSLog(#"operation for <%#> started.", _url);
[self willChangeValueForKey:#"isExecuting"];
_isExecuting = YES;
[self didChangeValueForKey:#"isExecuting"];
// Setup up the request with the url
NSMutableURLRequest *request = [[NSMutableURLRequest alloc]
initWithURL:_url];
[request setHTTPMethod:#"GET"];
[request setValue:[NSString stringWithFormat:#"Basic %#", encodedLoginData] forHTTPHeaderField:#"Authorization"];
_connection = [[NSURLConnection alloc] initWithRequest:request
delegate:self];
if (_connection == nil)
[self finish];
else {
_data = [[NSMutableData alloc] init];
}
}
- (void)finish
{
NSLog(#"operation for <%#> finished. "
#"status code: %d, error: %#, data size: %u",
_url, _statusCode, _error, [_data length]);
[_connection release];
_connection = nil;
[self willChangeValueForKey:#"isExecuting"];
[self willChangeValueForKey:#"isFinished"];
_isExecuting = NO;
_isFinished = YES;
[self didChangeValueForKey:#"isExecuting"];
[self didChangeValueForKey:#"isFinished"];
}
#pragma mark -
#pragma mark NSURLConnection delegate
- (void)connection:(NSURLConnection *)connection
didReceiveResponse:(NSURLResponse *)response
{
//[_data release];
//_data = [[NSMutableData alloc] init];
[_data setLength:0];
NSHTTPURLResponse * httpResponse = (NSHTTPURLResponse *)response;
_statusCode = [httpResponse statusCode];
}
- (void)connection:(NSURLConnection *)connection
didReceiveData:(NSData *)data
{
[_data appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// Parse the responseData of json objects retrieved from the service
SBJSON *parser = [[SBJSON alloc] init];
NSString *jsonString = [[NSString alloc] initWithData:_data encoding:NSUTF8StringEncoding];
NSDictionary *jsonData = [parser objectWithString:jsonString error:nil];
NSMutableArray *array = [jsonData objectForKey:#"Children"];
schedulesArray = [NSMutableArray array];
[schedulesArray addObject:array];
// Callback to AttendanceReportViewController that the responseData finished loading
[attendanceReportViewController loadSchedule];
[self finish];
}
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error
{
_error = [error copy];
[self finish];
}
#end
When all data is received I want to call back to my ViewController and get an array with all data from all request made.
Create a downloader
Create a downloader class using NSURLConnection.
Keep a member variable called downloaderObjectId.
Write a delegate method for this object. This method will pass the downloaded data and downloaderObjectId back to the delegate.
In the delegate.
Create multiple downloader objects(As per your ncessity) with unique value for the downloaderObjectId.
Store these objects in a NSMutableDictionary
Key for each object will be downloaderObjectId. so that when the delegate method is called after download you take the exact object back from the NSMutableDictionary using this key.
Main point.
Each time delegate is called. You should remove the object from dictionary(The object who is done with the download and called his delgate. You can identify this object by the key downloaderObjectId he holds. )
Then check the count of dictionary. If it is zero you can make sure that your downloads are completed. So you can call your viewcontroller.

Memory allocation, release and NSURLConnection in iPhone app

I'm hoping someone can help me with this. I'm struggling to find an answer to what should be an easy question. By the way, this is my first major obj-c project after years of using c and c# so feel free to point out things I'm failing on.
I have an iPhone app designed to load an album of photos into a UIScrollView. It also has a random image function which uses the same process but only displays a single image. It works like so:
Read an external XML feed (stored on ruby-on-rails website) which contains a path to a random url of photo once parsed.
Content of URL is downloaded using NSURLConnection to NSData.
ScrollView is created and pushed
Subclassed UIView allocs an UIImageView, allocs a UIImage with the NSData which, inits the UIImageView with the UIimage and finally adds the imageview to the UIView.
The parent view then adds the UIView to the UIScrollView which is then pushed to the front.
This process occurs again when when the next random image is required. It also uses the same process when displaying an entire album of images except several UIViews are added to the UIScrollView.
The problem is, despite using release and delloc where appropriate, the activity tool indicates that the memory used by NSURLConnection and UIImage isn't being released from memory when the next image is requested. This is further proven by building the app to the iPhone. Requesting several images in a row causes the app the crash presumably from memory consumption.
Below is some of the code as I'm unable to post the entire project due to contractual agreements.
URLDownload class (uses DataDownload)
#implementation URLDownload
-(NSData *)GetURL:(NSURL *)strURL
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
DataDownload *request = [DataDownload alloc];
request.strURL = strURL;
[request init];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
while (request.isLoading && [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
[pool release];
return [request urlData];
[request release];
}
DataDownload class
#implementation DataDownload
#synthesize urlData, connection, strURL, isLoading;
- (id)init
{
if(self = [super init])
{
self.isLoading = YES;
NSURLRequest *dataRequest = [NSURLRequest requestWithURL:self.strURL
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:60];
/* establish the connection */
self.connection = [[NSURLConnection alloc]
initWithRequest:dataRequest
delegate:self
];
if (connection == nil) {
self.urlData = nil;
} else {
self.urlData = [NSMutableData data];
}
}
return self;
}
- (void)dealloc {
[connection cancel];
[connection release];
[urlData release];
[strURL release];
[super dealloc];
}
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse {
return nil;
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse*)response
{
[urlData setLength:0];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData*)incrementalData
{
[self.urlData appendData:incrementalData];
}
-(void)connectionDidFinishLoading:(NSURLConnection*)connection
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
self.isLoading = NO;
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
self.isLoading = NO;
}
#end
ImageView subclass
#implementation ImageView
#synthesize strURL, imvImageView, image, currentlyRotated, imgOptionsView, startDate;
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.imvImageView = [UIImageView alloc];
if (self.strURL != nil){
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
NSData *datImageData = [NSData dataWithContentsOfURL: [NSURL URLWithString:self.strURL]];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
self.image = [UIImage imageWithData: datImageData];
CGSize imgSize = image.size;
CGFloat fltWidth = imgSize.width;
CGFloat fltHeight = imgSize.height;
[imvImageView initWithImage:image];
// If landscape rotate image
if (fltWidth > fltHeight){
imvImageView.frame = CGRectMake(-80.0, 80.0, 480.0, 320.0);
CGAffineTransform rotate = CGAffineTransformMakeRotation(-1.57079633);
[imvImageView setTransform:rotate];
self.currentlyRotated = YES;
}else{
imvImageView.frame = CGRectMake(0.0, 0.0, 320.0, 480.0);
self.currentlyRotated = NO;
}
[image release];
}else{
self.image = [UIImage alloc];
[imvImageView initWithImage:image];
[image release];
}
[self addSubview:imvImageView];
}
[imvImageView release];
return self;
}
- (void)drawRect:(CGRect)rect {
// Drawing code
}
- (void)dealloc {
[strURL release];
[imvImageView release];
[image release];
[imgOptionsView release];
[startDate release];
[super dealloc];
}
Sample code of how images are being displayed
- (void)displayImage:(NSString *)strURL {
NSString *strFullURL = #"http://www.marklatham.co.uk";
strFullURL = [strFullURL stringByAppendingString:strURL];
self.scroller = [[UIScrollView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 480.0)];
[scroller setContentSize:CGSizeMake(320.0, 540.0)];
[self.view addSubview:scroller];
CGRect rect = CGRectMake(0.0, 0.0, 320.0, 480.0);
self.imageView = [ImageView alloc];
imageView.strURL = strFullURL;
[imageView initWithFrame:rect];
[scroller addSubview:imageView];
[scroller release];
[imageView release];
}
The following images show all allocations to illustrate what's happening. 1 shows alloc on startup, 2 shows after first image is loaded and 3 after after second image.
http://www.gretanova.com/misc/iphone/1.png & 2.png & 3.png
Thanks everyone,
Lee
A couple of things stick out. For example within the URLDownload class the call to [request release] will never be reached as its placed after the return statement
return [request urlData];
[request release];