views:

838

answers:

3

Hello experts!

I'm trying to create a very simple app which connects to an URL and gets the contents of that URL, in this case a simple xml document. My problem is that that the request never seems to gets sent.

I have created a new project(Foundation tool) which runs from a main file. I have set the delegate of the NSURLConnection to self and implemented the necessary delegate methods. The problem is that these methods never get called. It doesn't matter if I change the URL to some bogus one(as in the example below). It seems to me that the application finish launching without sending or waiting for the response. Could it be that my application creates a thread for the request and then finishes without waiting for the response?? I know that the class is running since i get the NSLOG statements like "Created connection " but no NSLOG statements from within the delegate methods.

What am I missing?

#import "NetworkController.h"
@implementation NetworkController

- (id)init
{
    if(self = [super init])
    {

    }  
    return self;
}
- (void)dealloc
{
    NSLog(@"Calling dealloc");
    [super dealloc];
}
- (void) performConnection
{
    NSMutableData *receivedData;
    NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:
               @"http://www.test.php?some variables"]];

    NSMutableURLRequest *connectionRequest = [NSMutableURLRequest requestWithURL:url
                   cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:60.0];
    [url release];
    NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:connectionRequest delegate:self startImmediately:YES];

    if (!theConnection)
    {
     NSLog(@"Failed to submit request");
    }
    if(theConnection)
    {
     receivedData=[[NSMutableData data] retain];
     NSLog(@"Created connection.");

     NSLog(@"--------- Request submitted ---------");
     NSLog(@"receivedData: %@", receivedData);
    }

    [connectionRequest release];
}

#pragma mark NetworkController Delegate
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    NSLog(@"Received response: %@", response);
    NSLog(@"Received response, connection retain count is %d",[connection retainCount]);
}

#pragma mark NetworkController Delegate
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    NSLog(@"Connection received data, retain count: %d", [connection retainCount]);
}

#pragma mark NetworkController Delegate
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
    NSLog(@"finished connection retain count:  %d", [connection retainCount]);}

#pragma mark NetworkController Delegate
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    NSLog(@"Error receiving response: %@", error);
    [connection release];
}

@end

#import <Foundation/Foundation.h>
#import "NetworkController.h"

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];


    NetworkController *n = [[NetworkController alloc] init];
    n.performConnection;
    [n release];

    // insert code here...
    NSLog(@"Hello, World!");
    [pool drain];
    return 0;
}
A: 

wtf is this

n.performConnection;

?

Igor
He's abusing property access syntax to call a method. (Possibly without intending to.)
Peter Hosey
Strange, I didn’t know that works without declaring the property with @property. Apparently it does, I’ve just checked in Xcode.
zoul
+2  A: 

First, the message syntax is:

[n performConnection];

What you have there is property access syntax. And since the method is typed as returning void, I wouldn't take it as guaranteed to work even on that level.

Anyway, the major problem is that you're using the asynchronous NSURLConnection API (good) but expecting synchronous behavior (bad). All the NSURLConnection methods return immediately, then work in the background, with the idea sending delegate messages to your network controller as things happen—but immediately after you finish setting everything up, you release the network controller (in turn leaking the connection, since you didn't release it in your dealloc method), then exit.

You need to run the run loop before you release the network controller, to give the connection time to send the request, read the response, and send the network controller some delegate messages. You also need to cancel and release the connection in the network controller's dealloc method.

Also, you need to make that NSMutableData variable an instance variable, since you need to be able to reach it from the delegate methods. Don't forget to release that, too, in your dealloc method (and release it and set it to nil in both of the delegate methods that indicate the connection is over).

Peter Hosey
+1, this is better then my answer. Plus we should tell him not to monkey with main manually, unless he knows what he is doing.
zoul
He's writing a command-line tool; putting at least some code in main is normal for such a program.
Peter Hosey
Aha, that did not occur to me.
zoul
You're right, Peter, the connection does count as an input source. I recall it not working properly in 10.4 tho, so maybe it's been changed. Anyway, I am deleting my answer because I didn't read his code closely enough and it has a lot of issues that I think your answer addresses.
Jason Coco
A: 

Thank you for your answer, what i still don't understand is how to obtain the result before my app finishes launching. How do I get the main app to wait for the delegate of the networkcontroller to receive the data and then do something with it?

jakob