NSURLConnection needs a NSRunLoop to execute?

有些话、适合烂在心里 提交于 2019-11-28 06:16:57

问题


I'm trying to fetch the contents of a URL from a method called connect. It reads its configuration settings and attempts to fetch the URL. It appears to need the run loop to execute. I though it would just execute once and be done. It seems that I don't need to have that run loop running for any reason other than to fetch this URL once, whenever connect is called. Is there a better way do do this?

- (BOOL) connect 
{
    // read serverName and eventId from preferences
    NSMutableArray* preferences;
    preferences = [NSMutableDictionary dictionaryWithContentsOfFile:@"/tmp/wt.plist"];

    // if these values aren't nil or blank
    if ([preferences valueForKey:@"serverAddress"] && [preferences valueForKey:@"eventId"]) {

        [self setServerAddress:@"172.16.28.210"];
        [self setEventId:@"OT-3037009"];
    } else{
        // return some kind of error message
    }

    NSLog(@"VideoDataURL: %@", [self getVideoDataURL]);

    // grab the URL and query the server

    NSURL *myURL = [NSURL URLWithString:@"http://localhost"];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:myURL
                                                           cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
                                                       timeoutInterval:2];

    __unused NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];

    [[NSRunLoop currentRunLoop] run];

    return TRUE;
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    // This method is called when the server has determined that it
    // has enough information to create the NSURLResponse.

    // It can be called multiple times, for example in the case of a
    // redirect, so each time we reset the data.

    // receivedData is an instance variable declared elsewhere.
    [incomingData setLength:0];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    /* 
     * Called each time a chunk of data arrives
     */

    NSLog(@"received %lu bytes", [data length]);

    // Create a mutable data if it doesn't already exist
    if (!incomingData) {
        incomingData = [[NSMutableData alloc] init];
    }

    [incomingData appendData:data];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error 
{
    /*
     * Called if the connection fails
     */

    NSLog(@"connection failed: %@", [error localizedDescription]);  
    incomingData = nil;
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection 
{
    /*
     * Called when the last chunk has been processed
     */

    NSLog(@"Got it all!");

    NSString *string = [[NSString alloc] initWithData:incomingData encoding: NSUTF8StringEncoding];

    incomingData = nil;

    NSLog(@"response is: %@", string);
}

回答1:


If you're starting the connection off of the main thread, yes, this is correct. I just use GCD to create and start the connection on the main thread to avoid having to think about run loops.

dispatch_async(dispatch_get_main_queue(), ^{
    NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
    [conn start];
});

Works from anywhere and you don't have to keep track of run loops or threads.




回答2:


I would make sure you manually tell the runLoop of the worker thread you're spawning the connection on to run until the connection is finished. If you do not do this, Cocoa will automatically put the thread to sleep, hence why your delegate methods seem to never receive a response.

// The following is delcared within the scope of a NSThread, NSOperation, or GCD Block
//
// The NSURLConnection object and it's initialization/start methods
// |
// V
// Above the method call to the thread's runLoop.
//
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:<a time interval>];
// I use a minute, or 60.0, before I manually cancel the connection
// and send an error message back to the caller.

^Is the method you should call after you spawn your connection. It should give you the results you're looking for.

Keep this structure in mind when you're building asynchronous server calls/tasks:

Thread

RunLoop

Task

Each asynchronous task needs its own RunLoop in order for events to be continuously polled during the duration of its existence. Each RunLoop needs to exist within the scope of a thread in order for its associated task(s) to be executed.

You can build an asynchronous networking task manually using NSThread/NSRunLoop/NSURLConnection, Grand Central Dispatch, or even NSOperation/NSOperationQueue. I prefer Operations/Queues because I feel they are more Object-Oriented, and block-based programming can be kind of tricky (which is the structure you'd be writing your request in if you just used "dispatch_" calls). When it all comes down to it, write your threading model based on your programming preference. Each approach will give you the same result.

Also, do not use dispatch_sync unless you're performing the portion of a task that requires synchronization, or else you will lock-up the UI. Mastery of RunLoops is necessary in order to create a truly asynchronous model for your application.

Good luck!

P.S. NSURLConnection is already built to run asynchronously, but, to create a truly asynchronous networking model, your application will need to be spawning connections on threads separate from the Main Thread.




回答3:


I don't think synchronous requests need a run loop:

NSURLResponse* response = nil;
NSData* data = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&response error:nil];


来源:https://stackoverflow.com/questions/10535201/nsurlconnection-needs-a-nsrunloop-to-execute

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!