Subsequent NSURLConnections not firing delegate me

2019-06-07 17:53发布

问题:

I've looked all over for answers to this question and have seen a lot of similar issues but the solutions did not seem to pertain to my situation. I'm very new to iOS development so any help would be appreciated.

The current app is looking to display events happening in a city based on an XML feed being generated by their website. I'm storing the events in a plist and checking for new events explicitly on app load. The current process is as follows:

  • Application launches and makes a NSURLConnection to check the server for a timestamp and download it.
  • Checks the newly downloaded timestamp vs the local timestamp to see if it is greater. If it is, it rewrites the file containing the plist and fires a notification that new events are needed.

This is where the second NSURLConnection does not seem to fire. The callback method itself fires but it does not seem like the delegate methods fire after.

The strange thing is that if I call both connections at the same time, as opposed to one as a result of the other, they both will fire and call the delegate methods.

Here is the code for some of the classes with some of the guts removed for clarity:

Application Delegate:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    

// Set up analytics account

// Set the tab bar controller as the window's root view controller and display.
self.window.rootViewController = self.tabBarController;
[self.window makeKeyAndVisible];

//Set up listeners for event handling
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(timeStampResponse:) name:@"TimeStampResponse" object:nil];    

//Set app loading screen

//Create new OperationQueue, set ActivityIndicator and run TimeStamp Check
parseQueue = [NSOperationQueue new];    
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[self timeStampCheck];  
return YES;
}
-(void)timeStampCheck {
NSLog(@"Time Stamp Check, Creating Connection");
NSURLRequest *timeStampURLRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:kTimeStampURL]];
self.timeStampConnection = [[NSURLConnectionWithTag alloc] initWithRequest:timeStampURLRequest delegate:self startImmediately:YES tag:1];
}

-(void)newEventsNeeded {
NSLog(@"Event File Doesn't Exists or New File Needed, Creating Connection");
NSURLRequest *feedURLRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:kEventFeedURL]];
self.eventsFeedConnection = [[NSURLConnectionWithTag alloc] initWithRequest:feedURLRequest delegate:self startImmediately:YES tag:0];
}

//Callback from DateStampParser
-(void)timeStampResponse:(NSNotification *)notif {
NSString *response = [[notif userInfo] objectForKey:@"TimeStampResponse"];
if ([response isEqualToString:@"NewEvents"]) {
    [self newEventsNeeded];
    self.appLoadingViewController.loadingLabel.text = @"New Events Found...";
} else if ([response isEqualToString:@"NoNewEvents"]){
    [self allEventsLoaded];
}
}

And the NSURLConnection Delegate methods

-(void)connection:(NSURLConnectionWithTag *)connection didFailWithError:(NSError *)error {
if (connection.tag == 0) {
    NSLog(@"Connection failed on Event Feed");
} else if (connection.tag == 1) {
    NSLog(@"Connection failed on Time Stamp Check");
}
}

-(void)connection:(NSURLConnectionWithTag *)connection didReceiveResponse:(NSURLResponse *)response{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
NSLog(@"connection:didRecieveResponse:NSHTTPURLResponse");
if ((([httpResponse statusCode]/100) == 2)) {
    //Handling for eventsFeedConnection
    if (connection.tag == 0) {

        NSLog(@"Connection tag = 0");
        self.eventsData = [NSMutableData data];

    } else if (connection.tag == 1) {

        NSLog(@"Connection tag = 1");
        //NSLog(@"Timestamp connection received response");
        self.timeStampData = [NSMutableData data];

    } 

} else {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Network Error" message:@"No Response from the Server" delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil];
    [alert show];
    [alert release];
}
}

-(void)connection:(NSURLConnectionWithTag *)connection didReceiveData:(NSData *)data{
//Handling for eventsFeedConnection
if (connection.tag == 0) {

    self.appLoadingViewController.loadingLabel.text = @"Downloading Events...";
    [eventsData appendData:data];

} else if (connection.tag == 1) {

    self.appLoadingViewController.loadingLabel.text =@"Checking for New Events...";
    [timeStampData appendData:data];

}   
}

-(void)connectionDidFinishLoading:(NSURLConnectionWithTag *)connection{
//Handling for eventsFeedConnection
if (connection.tag == 0) {

    NSLog(@"EventFeed Connection Finished");
    self.eventsFeedConnection = nil;
    [eventsFeedConnection release];
    ParserOperation *parser = [[ParserOperation alloc] initWithData:self.eventsData];
    [parseQueue addOperation:parser];
    [parser release];
    self.eventsData = nil;
    //[eventCategoryListViewController reenableRefreshButton];

} else if (connection.tag ==1){

    NSLog(@"TimeStamp Connection Finished");
    self.timeStampConnection = nil;
    [timeStampConnection release];
    DateStampParser *parser = [[DateStampParser alloc] initWithData:self.timeStampData];
    [parseQueue addOperation:parser];
    [parser release];
    self.timeStampData = nil;

} 

}

Not sure if it may be obvious from just this code or not what the problem is but if any additional code is needed/clarification on what I'm trying to accomplish is needed please let me know.

Thanks

edit for clarity Just to clarify, the NSLog from newEventsNeeded fires, but just the delegate methods don't get fired.

回答1:

Make sure the thread starting the NSURLConnections has a runloop. NSURLConnection's asynchronous callbacks are dispatched onto the calling threads runloop. If the calling thread (in this case, it seems to be an NSOperation queue) doesn't have a runloop, or if it's not operating in the default run loop mode, you won't see any delegate callbacks. Your first call to [self timeStampCheck] is on the main thread, so that's fine. Your later calls are from the operation queue, which possibly has no runloop.

The relevant snippet from the documentation:

delegate

The delegate object for the connection. The delegate will receive delegate messages as the load progresses. Messages to the delegate will be sent on the thread that calls this method. For the connection to work correctly, the calling thread’s run loop must be operating in the default run loop mode.