4

我想在后台线程上的 NSOperation 内执行异步 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{

}

我没有得到服务器的响应。

4

3 回答 3

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];
}

更多细节可以在这里找到:链接

于 2013-07-02T13:42:04.160 回答
2

几种方法:

  1. NSURLConnection在主运行循环中安排,通过使用 的startImmediately参数NO,设置运行循环,然后才应该开始连接,例如:

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

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


因此,AFNetworking 会执行以下操作:

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

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

@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];
}
于 2013-07-02T13:44:31.887 回答
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() 与 [NSRunLoop 运行]

于 2014-06-25T12:01:14.617 回答