如何在NSOperation中启动异步NSURLConnection?

时间:2013-07-02 13:26:01

标签: iphone objective-c connection nsoperation nsoperationqueue

我想在后台线程的NSOperation中进行Asynchrous NSURLConnection。 这是因为我们在回来时对数据做了一些非常昂贵的操作。

这是他们在这里问的非常相似的问题: How do I do an Asynchronous NSURLConnection inside an NSOperation?

但区别在于我在另一个类中运行连接。

这是我的第一次尝试:

在我的MainViewController中:

@property (nonatomic, strong) NSOperationQueue *requestQueue;

#pragma mark - Lazy initialization
- (NSOperationQueue *)requestQueue 
{
    if (!_requestQueue) {
        _requestQueue = [[NSOperationQueue alloc] init];
        _requestQueue.name = @"Request Start Application Queue";
        _requestQueue.maxConcurrentOperationCount = 1;
    }
    return _requestQueue;
}

-(void)callToServer
{
URLJsonRequest *request = [URLRequestFactory createRequest:REQUEST_INTERFACE_CLIENT_VERSION
                                                         delegate:self];

    RequestSender *requestSender = [[RequestSender alloc]initWithPhotoRecord:request delegate:self];

   [self.requestQueue addOperation:requestSender];
}

这是我的操作:

- (id)initWithPhotoRecord:(URLJsonRequest *)request
                 delegate:(id<RequestSenderDelegate>) theDelegate{

    if (self = [super init])
    {
        self.delegate = theDelegate;
        self.jsonRequest = request;
    }
    return self;
}

- (void)main {

    //Apple recommends using @autoreleasepool block instead of alloc and init NSAutoreleasePool, because blocks are more efficient. You might use NSAuoreleasePool instead and that would be fine.
    @autoreleasepool
    {

        if (self.isCancelled)
            return;

        [self.jsonRequest start];

    }
}

这是我的请求启动功能:

-(void) start
{
  NSURL *url = [NSURL URLWithString:@"http://google.com"];
 NSURLRequest *theRequest = [NSURLRequest requestWithURL:url];
  urlConnection = [[[NSURLConnection alloc]    initWithRequest:theRequest delegate:self]autorelease];

[urlConnection start];
[theRequest release]
}


- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
    NSLog(@"Received reponse from connection");
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{



}

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

}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{

}

我没有收到服务器的回复。

3 个答案:

答案 0 :(得分:2)

-(void)start
{
  [self willChangeValueForKey:@"isExecuting"];
  _isExecuting = YES;
  [self didChangeValueForKey:@"isExecuting"];
  NSURL* url = [[NSURL alloc] initWithString:@"http://url.to/feed.xml"];
  NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20];
  _connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]; // ivar
  [request release];
  [url release];
  // Here is the trick
  NSPort* port = [NSPort port];
  NSRunLoop* rl = [NSRunLoop currentRunLoop]; // Get the runloop
  [rl addPort:port forMode:NSDefaultRunLoopMode];
  [_connection scheduleInRunLoop:rl forMode:NSDefaultRunLoopMode];
  [_connection start];
  [rl run];
}

可在此处找到更多详细信息:link

答案 1 :(得分:2)

有两种方法:

  1. 在主运行循环中安排NSURLConnection,使用startImmediately的{​​{1}}参数,设置运行循环,然后才开始连接,例如:

    NO
  2. 为连接创建专用线程,并在为该线程创建的运行循环中安排连接。有关此示例,请参阅AFNetworking来源中的urlConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self startImmediately:NO]; [urlConnection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; [urlConnection start];

  3. 实际上使用AFNetworking,它可以为您提供可以添加到队列中的基于AFURLConnectionOperation.m的操作,并为您处理此运行循环内容。


  4. 所以,AFNetworking做了类似的事情:

    NSOperation

    所以我做了类似下面的事情。首先,我有一些私人财产:

    + (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;
    }
    

    然后网络操作可以执行以下操作:

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

答案 2 :(得分:0)

我知道这篇文章已有一年多的历史了,但我想为那些可能在尝试创建自己的异步网络操作时遇到同样问题的人添加一些建议。 您需要将runloop添加到在后台运行的操作,并且应该在操作完成后停止它。

实际上有两个简单的选项:

选项1 - 使用NSRunLoop

NSPort *port        = [NSPort port];
NSRunLoop *runLoop  = [NSRunLoop currentRunLoop];
[runLoop addPort:port forMode:NSDefaultRunLoopMode];
[self.connection scheduleInRunLoop:runLoop forMode:NSDefaultRunLoopMode];
[self.connection start];
[runLoop run];

并且您需要在操作完成时停止:

NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
NSDate *date       = [NSDate distantFuture];
while (!runLoopIsStopped && [runLoop runMode:NSDefaultRunLoopMode beforeDate:date]);

选项2 - 使用CF

您需要添加

CFRunLoopRun();

开始操作时

并致电

CFRunLoopStop(CFRunLoopGetCurrent());

完成手术后。

阅读以下帖子:CFRunLoopRun() vs [NSRunLoop run]