天天看點

異步網絡請求

網絡請求被當作是比較耗時的操作,是以建議所有的網絡操作都處理成異步的,并且在背景線程中來執行。在iOS開發中,實作異步的方法常見的有兩種:

1、使用GCD的方式

2、使用NSOperation來做。

下面詳細地介紹一下:

All your networking should be done asynchronously. However, with Grand Central Dispatch, you sometimes see code like this:

// Warning: please don't use this code.

dispatch_async(backgroundQueue, ^{

    NSData *contents = [NSData dataWithContentsOfURL:url];

    dispatch_async(dispatch_get_main_queue(), ^{

        // do something with the data

    });

});

以上方法最大的缺點就是不能執行取消操作

This might look quite smart, but there is a big problem with this code: there is no way to cancel this synchronous network call. It will block the thread until it's done. In case operation times out, this might take a very long time (e.g. dataWithContentsOfURL has a timeout of 30 seconds).

If the queue is a serial queue, then it will be blocked for the whole time. If the queue is concurrent, then GCD has to spin up a new thread in order to make up for the thread which you are blocking. Both cases are not good. Its best to avoid blocking altogether.

利用NSOperation,可以實作對并發任務數的控制,管理任務之間的依賴關系, 同時還可以取消一個任務。

To improve upon this situation, we will use the asynchronous methods of NSURLConnection and wrap everything up in an operation. This way we get the full power and convenience of operation queues; we can easily control the number of concurrent operations, add dependencies, and cancel operations.

However, there is something to watch out for when doing this: URL connections deliver their events in a run loop. It is easiest to just use the main run loop for this, as the data delivery doesn't take much time. Then we can dispatch the processing of the incoming data onto a background thread.

Another possibility is the approach that libraries like AFNetworking take: create a separate thread, set up a run loop on this thread, and schedule the url connection there. But you probably wouldn't want to do this yourself.

To kick off the URL connection, we override the start method in our custom operation subclass:

- (void)start {

    NSURLRequest *request = [NSURLRequest requestWithURL:self.url];

    self.isExecuting = YES;

    self.isFinished = NO;

    [[NSOperationQueue mainQueue] addOperationWithBlock:^{

        self.connection = [NSURLConnection connectionWithRequest:request delegate:self];

    }];

}

Since we override the start method, we now must manage the operation's state properties, isExecuting and isFinished, ourselves. To cancel an operation, we need to cancel the connection and then set the right flags so the oepration queue knows the operation is done.

- (void)cancel {

    [super cancel];

    [self.connection calcel];

    self.isFinished = YES;

    self.isExecuting = NO;

When the connection finishes loading, it sends a delegate callback:

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {

    self.data = self.buffer;

    self.buffer = nil;

And that's all there is to it.To conclude, we would like to recommend either taking your time to do this right, or to use a library like AFNetworking. They provide handy utilities like a category on UIImageView that asynchronously loads an p_w_picpath from a URL. Using this in your table view code will automatically take care of canceling p_w_picpath loading operations.

繼續閱讀