NSURLConnection, delegate methods dont get called

Discussion in 'iOS Programming' started by estupefactika, May 14, 2009.

  1. estupefactika macrumors member

    Joined:
    Feb 16, 2009
    Location:
    Alcobendas (Madrid)
    #1
    Hi, I've setup a class as a delegate for NSURLConnection, I
    create the NSURLConnection, provide it a delegate (self) but the
    delegate methods don't get called (didReceiveData and DidFinishLoading), any idea? Thanks

    Code:
    
    //In my method
    for (int i=0;i<[feedArray count];i++) {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    		imgUrl = [NSURL URLWithString:[[mainDelegate.feedArray objectAtIndex:i] objectForKey:@"img"]];
    
    		AsyncImageView *async=[[AsyncImageView alloc] init];
    		[async loadImageFromURL:imgUrl];
    		[async release];
    [pool release];			
    }
    
    
    
    
    //My class
    @implementation AsyncImageView
    @synthesize urlImg;
    - (id) init
    {
    	
    	if (self = [super init]) { 		
    		appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
    	}
    	return self; 
    }
    
    
    - (void)loadImageFromURL:(NSURL*)url{
    	
        if (connection!=nil) { [connection release]; }
        if (data!=nil) { [data release]; }
        NSURLRequest* request = [NSURLRequest requestWithURL:url 		 cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
    
    	connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
    	
    //I can see in logs some like:
    //<NSURLConnection: 0x10d19d0, http://www.myurl.com/images/thumbnails/08_01.jpg>
    
    
        //TODO error handling, what if connection is nil?
    }
    
    
    - (void)connection:(NSURLConnection *)theConnection	didReceiveData:(NSData *)incrementalData {
        if (data==nil) {
    		data = [[NSMutableData alloc] initWithCapacity:2048];
        }
        [data appendData:incrementalData];
    }
    
    - (void)connectionDidFinishLoading:(NSURLConnection*)theConnection {
    	
    	
        [connection release];
        connection=nil;
    	UIImage *img=[UIImage imageWithData:data];
    	.....
    	
        [data release];
        data=nil;
     
    }
    
    - (void)dealloc {
        [connection cancel];
        [connection release];
        [data release];
        [super dealloc];
    }
    
    
     
  2. robbieduncan Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #2
    What about the other delegate methods. In particular connection:didFailWithError:?
     
  3. jnic macrumors 6502a

    Joined:
    Oct 24, 2008
    Location:
    Cambridge
  4. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #4
    Actually, I believe the signatures are okay (parameter names don't have to match the ref) but the problem is "theConnection" is the parameter name for
    Code:
    -(void)connectionDidFinishLoading:(NSURLConnection*)theConnection
    but then the code of the method uses "connection". So, you'd want:
    Code:
    - (void)connectionDidFinishLoading:(NSURLConnection*)theConnection {
    	
    	
        [theConnection release];
        theConnection =nil;
    	UIImage *img=[UIImage imageWithData:data];
    	.....
    	
        [data release];
        data=nil;
     
    }
    
     
  5. jnic macrumors 6502a

    Joined:
    Oct 24, 2008
    Location:
    Cambridge
    #5
    Cool, learnt something new :)
     
  6. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #6
    Not that I recommend using different names. I normally copy the signature right out of the class reference just to make sure I am getting it exactly right.
     
  7. estupefactika thread starter macrumors member

    Joined:
    Feb 16, 2009
    Location:
    Alcobendas (Madrid)
    #7
    Ok, Ive tried this, now Im testint retainCount of connection. I have a question, why when I alloc a new connection the retainCount is 2??

    connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
    NSLog(@"%d",[connection retainCount]); //Result is 2. Shouldn't it be 1?


    I think delegate dont works because I use this class twice. When I alloc the connection retainCount is 2 (I dont know why..), the firts time it works the methods delegate, but method dealloc is not called, the objects arent released because retaintcount dont down to 0, so when I use the class by second time, once loadImageFromUrl has finished, method dealloc is called immediately and release the objects... by this delegate dont works, the connection is cancel

    My question is Why when I alloc the connection retainCount is 2? Sorry for my english...
     
  8. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #8
    Please see this post.
     
  9. jnic macrumors 6502a

    Joined:
    Oct 24, 2008
    Location:
    Cambridge
  10. estupefactika thread starter macrumors member

    Joined:
    Feb 16, 2009
    Location:
    Alcobendas (Madrid)
    #10
    Yes, sorry. Nothing happens, this method is not called nor, no errors.

    Once loadImageFromUrl has finished, immediatelly dealloc release the objects and methods delegate arent't called.

    Code:
    - (void)connection:(NSURLConnection *)theConnection didFailWithError:(NSError *)error 
    { 
    	// release the connection, and the data object
    	[theConnection release]; 
    	// receivedData is declared as a method instance elsewhere 
    	[data release]; 
    	// inform the user 
    	NSLog(@"Connection failed! Error - %@ %@", 
    		  [error localizedDescription], 
    		  [[error userInfo] objectForKey:NSErrorFailingURLStringKey]); 
    }
    
     
  11. kainjow Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #11
    This is the problem:
    Code:
    AsyncImageView *async=[[AsyncImageView alloc] init];
    [async loadImageFromURL:imgUrl];
    [async release];
    You're releasing the object before the connection finishes.

    Either refactor your code, or throw in some NSRunLoop magic (which I suggest not doing).
     
  12. Peter Maurer macrumors member

    Joined:
    Oct 9, 2008
    #12
    Actually, I think the problem is that you never -start the connection. While NSURLConnection's convenience method +sendSynchronousRequest:returningResponse:error: starts loading automatically, most of the other ways to create a connection do not. (Hint: there's also -initWithRequest:delegate:startImmediately:, which should be just what you're looking for. ;))

    EDIT: Nevertheless, the memory management error kainjow mentions needs fixing, too -- unless you plan on loading stuff synchronously, which your class's name certainly doesn't suggest.
     
  13. kainjow Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #13
    I don't think that's a problem. According to the docs on the initWithRequest:delegate: method:

    Returns an initialized URL connection and begins to load the data for the URL request.​
     
  14. Peter Maurer macrumors member

    Joined:
    Oct 9, 2008
    #14
    Oops, you're right. My bad, sorry. That's indeed what the docs say.

    (Once the memory management stuff is fixed, estupefactika, and just in case it still doesn't work then: Does your delegate get a -connection:didReceiveResponse: message?)
     
  15. estupefactika thread starter macrumors member

    Joined:
    Feb 16, 2009
    Location:
    Alcobendas (Madrid)
    #15
    Ive removed it and it still dont works.
     
  16. estupefactika thread starter macrumors member

    Joined:
    Feb 16, 2009
    Location:
    Alcobendas (Madrid)
    #16
    Ive added:

    Code:
    - (void)connection:(NSURLConnection *)theConnection didFailWithError:(NSError *)error 
    { 
    	// release the connection, and the data object
    	[theConnection release]; 
    	// receivedData is declared as a method instance elsewhere 
    	[data release]; 
    	// inform the user 
    	NSLog(@"Connection failed! Error - %@ %@", 
    		  [error localizedDescription], 
    		  [[error userInfo] objectForKey:NSErrorFailingURLStringKey]); 
    }
    
    - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)respons {
    	NSLog(@"%@",respons);
    }
    
    - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
    	NSLog(@"%@",challenge);
    }
    
    They never aren't called.

    Im using this class twice. The first time it works fine, I have a table with an image in each cell, each one it inits the connection, I receive data and it finish loading the image in the cell.

    In the second time Im downloading the rest of images in background, is here where it dont works. I have a itineration "for" where I init the connection for each image. Once this method finish:
    Code:
    - (void)loadImageFromURL:(NSURL*)url {
      
        if (connection!=nil) {NSLog(@"0");[connection release]; }
        if (data!=nil) {NSLog(@"1");[data release]; }
        NSURLRequest* request = [NSURLRequest requestWithURL:url 	 cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
        connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
    }
    
    Inmediatelly the method dealloc is called, it is executed a couple of times:
    Code:
    - (void)dealloc {
        [connection cancel];
        [connection release];
        [data release];
        [super dealloc];
    }
    
    and no delegates are called, nor didFailWithError, nor didReceiveResponse, nor didReceiveAuthenticationChallenge, nor didReceiveData.

    Im going to try to make other class to download the rest of images in background, maybe there is any conflict
     
  17. estupefactika thread starter macrumors member

    Joined:
    Feb 16, 2009
    Location:
    Alcobendas (Madrid)
    #17
    I think I've found the problem, where I was calling to my downloadImages method there was a nsautoreleasepool, I think by this my connection was being released. Now I call my method out and it works fine. Thanks

    Code:
    
    - (void) doNewThreadStuff {
    
    	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    		
    	if ([mainDelegate.feedArray count] == 0) {
    		//Parser xml	
    		
    		ParserXML *parser=[[ParserXML alloc] init];
    		NSString *path=[[NSString alloc] initWithString:URL_XML];
    		[parser parseXMLFileAtURL:path];
                    //I was calling downloadImages here		
    		//[self downloadImages];
    		[parser release];
            }
    	
    	[pool release];
    	[self performSelectorOnMainThread:@selector(newThreadDone) withObject:nil waitUntilDone:NO];
    	
    }
    
    -(void) newThreadDone {
       [self downloadImages];
    }
    
    
    -(void) downloadImages {
    for (int i=0;i<[mainDelegate.feedArray count];i++) {
    		imgUrl = [NSURL URLWithString:[[mainDelegate.feedArray objectAtIndex:i] objectForKey:@"img"]];
    		AsyncImageView *async=[[AsyncImageView alloc] init];
    		[async loadImageFromURL:imgUrl];
    		[async release];
    }
    
    }
    
     
  18. cstromme macrumors regular

    Joined:
    Feb 26, 2007
    #18
    Sorry for bumping this thread, but I'm having a very similar problem and I thought it might be better to use this thread than to create a new one.

    I have imported a class from this example, but I'm having some problems with it.

    Basically I want to use this to fetch some data for an iPhone app, so I decided to first just try and use the class in a command line utility. So I created a new project in XCode, added a new class (KommendeFilmer) and used the code from the NewItemsClient class in the linked project. The problem is that it doesn't appear to fire off any of the delegate methods at all.

    Here's my KommendeFilmer.m:
    Code:
    #import <Foundation/Foundation.h>
    #import "Fetch.h"
    
    int main (int argc, const char * argv[]) {
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    
        // insert code here...
        Fetch *fetch = [[Fetch alloc] init];
    	
        [pool drain];
        return 0;
    }
    
    And here's my Fetch.m:
    Code:
    //
    //  Fetch.m
    //  KommendeFilmer
    //
    //  Created by Christian A. Strømmen on 29.07.09.
    //  Copyright 2009 __MyCompanyName__. All rights reserved.
    //
    
    #import "Fetch.h"
    
    
    @implementation Fetch
    
    @synthesize newItems;
    
    - (void)dealloc
    {
    	[newItems release];
    	[responseData release];
    	[baseURL release];
    	[super dealloc];
    }
    
    - (id)init
    {
    	if(self = [super init])
    	{
    		responseData = [[NSMutableData data] retain];
    		baseURL = [[NSURL URLWithString:@"http://store.apple.com"] retain];
    	
    		NSURLRequest *request = [NSURLRequest requestWithURL:baseURL];
    		[[NSURLConnection alloc] initWithRequest:request delegate:self];
    		NSLog(@"Test");
    	}
    	return self;
    }
    
    - (NSURLRequest *)connection:(NSURLConnection *)connection
    			 willSendRequest:(NSURLRequest *)request
    			redirectResponse:(NSURLResponse *)redirectResponse
    {
        [baseURL autorelease];
        baseURL = [[request URL] retain];
    	NSLog(@"Test 2");
        return request;
    }
    
    - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
    {
        [responseData setLength:0];
    	NSLog(@"Test 3");
    }
    
    - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
    {
        [responseData appendData:data];
    	NSLog(@"Test 4");
    }
    
    - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
    {
    //    [[NSAlert alertWithError:error] runModal];
    	NSLog(@"Test 5");
    }
    
    - (void)connectionDidFinishLoading:(NSURLConnection *)connection
    {
    	NSLog(@"Connection did finish loading");
        // Once this method is invoked, "responseData" contains the complete result
    	NSError *error;
    	NSXMLDocument *document =
    	[[NSXMLDocument alloc] initWithData:responseData options:NSXMLDocumentTidyHTML error:&error];
    	
    	// Deliberately ignore error: with most HTML it will be filled numerous
    	// "tidy" warnings.
    	
    	NSXMLElement *rootNode = [document rootElement];
    	
    	NSString *xpathQueryString =
    	@"//div[@id='newtothestore']/div[@class='modulecontent']/div[@class='list_content']/ul/li/a";
    	NSArray *newItemsNodes = [rootNode nodesForXPath:xpathQueryString error:&error];
    	
    	if (error)
    	{
    //		[[NSAlert alertWithError:error] runModal];
    		return;
    	}
    	
    	[self willChangeValueForKey:@"newItems"];
    	[newItems release];
    	newItems = [[NSMutableArray array] retain];
    	for (NSXMLElement *node in newItemsNodes)
    	{
    		NSString *relativeString = [[node attributeForName:@"href"] stringValue];
    		NSURL *url = [NSURL URLWithString:relativeString relativeToURL:baseURL];
    		
    		NSString *linkText = [[node childAtIndex:0] stringValue];
    		
    		[newItems addObject:
    		 [NSDictionary dictionaryWithObjectsAndKeys:
    		  [url absoluteString], @"linkURL",
    		  linkText, @"linkText",
    		  nil]];
    	}
    	[self didChangeValueForKey:@"newItems"];
    }
    
    @end
    
    Now, the only log messages I'm getting are "Test". Why aren't any of the delegate methods being run?


    Ps. Ignore most of the code in the delegate methods as I haven't changed anything yet, just trying to get it to actually use the delegate methods before I start tinkering with that.
     
  19. robbieduncan Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #19
    Because NSURLConnection is asynchronous. It needs a RunLoop to function and takes some time for stuff to happen. You create one then your program instantly ends.
     
  20. temawito macrumors newbie

    Joined:
    Nov 23, 2009
    #21
    at the end you are not using a background thread?

    Im having similar problem, when I do connection with the main thread it works, but background connection (another thread) I dont get a call back. What I understood from your answer is that you at the end use the main thread, am I wrong? someone can tell me what happens in this situations? Regards.
     
  21. jkichline macrumors 6502

    Joined:
    Aug 25, 2010
    #22
    The problem...

    The problem is that you are running this on another thread. Make sure you run NSURLConnection stuff on the main thread, otherwise it does not work correctly.
     

Share This Page