How do I start an Asynchronous NSURLConnection inside an NSOperation?

前端 未结 3 555
南笙
南笙 2021-01-06 06:27

I want to do an Asynchrous NSURLConnection inside of an NSOperation on a background thread. it is because I\'m doing some very expensive operations on the data as they come

3条回答
  •  别那么骄傲
    2021-01-06 07:15

    A couple of approaches:

    1. Schedule the NSURLConnection in the main run loop, by using the startImmediately parameter of NO, set the run loop, and only then should you start the connection, e.g.:

      urlConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self startImmediately:NO];
      [urlConnection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
      [urlConnection start];
      
    2. Create a dedicated thread for the the connection and schedule the connection in the run loop you create for that thread. See AFURLConnectionOperation.m in AFNetworking source for an example of this.

    3. Actually use AFNetworking, which gives you NSOperation based operations that you can add to your queue, and takes care of this run loop stuff for you.


    So, AFNetworking does something like:

    + (void)networkRequestThreadEntryPoint:(id)__unused object {
        @autoreleasepool {
            [[NSThread currentThread] setName:@"NetworkingThread"];
    
            NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
            [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
            [runLoop run];
        }
    }
    
    + (NSThread *)networkRequestThread {
        static NSThread *_networkRequestThread = nil;
        static dispatch_once_t oncePredicate;
    
        dispatch_once(&oncePredicate, ^{
            _networkRequestThread = [[NSThread alloc] initWithTarget:self
                                                            selector:@selector(networkRequestThreadEntryPoint:)
                                                              object:nil];
            [_networkRequestThread start];
        });
    
        return _networkRequestThread;
    }
    

    So I do something like the following. First I have a few private properties:

    @property (nonatomic, readwrite, getter = isExecuting)  BOOL executing;
    @property (nonatomic, readwrite, getter = isFinished)   BOOL finished;
    @property (nonatomic, weak)   NSURLConnection *connection;
    

    Then the network operation can then do something like:

    @synthesize executing = _executing;
    @synthesize finished  = _finished;
    
    - (instancetype)init {
        self = [super init];
        if (self) {
            _executing = NO;
            _finished = NO;
        }
        return self;
    }
    
    - (void)start {
        if (self.isCancelled) {
            [self completeOperation];
            return;
        }
    
        self.executing = YES;
    
        [self performSelector:@selector(startInNetworkRequestThread)
                     onThread:[[self class] networkRequestThread]
                   withObject:nil
                waitUntilDone:NO];
    }
    
    - (void)startInNetworkRequestThread {
        NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:self.request
                                                                      delegate:self
                                                              startImmediately:NO];
        [connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
        [connection start];
    
        self.connection = connection;
    }
    
    - (void)completeOperation {
        self.executing = NO;
        self.finished = YES;
    }
    
    - (void)setFinished:(BOOL)finished {
        if (finished != _finished) {
            [self willChangeValueForKey:@"isFinished"];
            _finished = finished;
            [self didChangeValueForKey:@"isFinished"];
        }
    }
    
    - (void)setExecuting:(BOOL)executing {
        if (executing != _executing) {
            [self willChangeValueForKey:@"isExecuting"];
            _executing = executing;
            [self didChangeValueForKey:@"isExecuting"];
        }
    }
    
    - (BOOL)isConcurrent {
        return YES;
    }
    
    - (BOOL)isAsynchronous {
        return YES;
    }
    
    // all of my NSURLConnectionDataDelegate stuff here, for example, upon completion:
    
    - (void)connectionDidFinishLoading:(NSURLConnection *)connection {
        // I call the appropriate completion blocks here, do cleanup, etc. and then, when done:
    
        [self completeOperation];
    }
    

提交回复
热议问题