7

我正在使用异步GCD发送请求。HTTP这是不起作用的代码:

dispatch_async(connectionQueue, ^{
        NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];

        [request setURL:[NSURL URLWithString:[NSString stringWithFormat:someURL]]];


        NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
        [connection start];//Not working
    }); 

上面的代码根本不起作用。我在 NSURLConnectionDelegate 的方法中没有收到任何回叫。

但是当我尝试以下代码时,一切正常,我得到了正确的回调

NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];

[request setURL:[NSURL URLWithString:[NSString stringWithFormat:someURL]]];

NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];

dispatch_async(connectionQueue, ^{

    [connection start]; // working fine. But WHY ????
});

有人可以解释块/ GCD的这种奇怪行为吗?

4

4 回答 4

2

在代码示例的第一部分尝试这个 -

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

如果您将连接放在后台队列中,它会在队列完成后被推开,因此您不会获得委托回调。连接可以在主队列中,因此它留在主运行循环中以发生回调。或者,您可以创建自己的运行循环,按照其他人的建议为您处理后台操作。

于 2012-10-25T19:35:09.410 回答
2

当使用 NSURLConnection 进行异步通信时,您需要让它实例化的线程将连接附加到它的 RunLoop 以便让该线程轮询同一连接的委托方法。

不依赖主线程的 RunLoop 异步实例化 NSURLConnection 的正确方法如下:

// Done within a Grand Central Dispatch block, or NSOperation.

// We do not start the connection here because we still need to attach the connection to the RunLoop of the current thread and handle how it will communicate responses back to the caller.   
theConnection = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:NO];

// A previously instantiated NSOperationQueue for managing the delegate callbacks if-and-when they occur.
// [theConnection setDelegateQueue:delegateQueue];
/*
// Other NSURLConnection logic, etc.
*/    
// We start the connection manually after properly establishing how it will poll and respond to events.
[theConnection start];

// This is for the RunLoop of the current thread. (Only needed for < iOS 6 compatibility)
// If this method is executed inside a GCD block or NSOperation, it will be the RunLoop of the thread run in-parallel to the Main Thread.
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:30.0]];
// From here, you can alter the time interval to coincide with a specific "time-out" event you'd like to occur.

其中“theConnection”是“NSURLConnection”类型的当前类的成员变量。此外,一旦您的连接收到响应,您将需要创建一个 NSOperationQueue 成员变量来管理委托回调。这些调用将异步传回运行连接的线程。

从那里,您可以使用正确的 NSURLConnection 委托方法返回数据。

为线程使用 Grand Central Dispatch 或 Operation Queues 的好处是已经内置了 Threading 和 RunLoop 机制;您不必手动分配一个带有自己的 RunLoop 的附加线程。这消除了创建后台线程来管理异步服务器调用的两步冗余。

我希望这足以让您走上正确的道路,为您的应用程序创建一个真正的异步网络模型。:)

于 2013-05-07T17:39:22.833 回答
1

NSURLConnection 将始终在创建它的线程(alloc init)上执行数据获取。这解释了为什么它会以第二种方式工作。第一种方法确实有效,但是在您能够从 NSURLConnection 接收任何信息之前,线程就死了。NSURLConnection 已经允许异步下载,但是如果您甚至想异步运行数据处理,您应该使用以下方法:

+ (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler

该方法有一些限制,例如身份验证受到限制,并且您无法跟踪迄今为止已下载了多少文档。您还必须指定您创建的 NSOperationQueue,默认队列是主循环队列。

于 2012-10-25T19:48:07.353 回答
1

您可以为该连接启动一个临时的 NSOperationQueue。该队列仅在连接需要时才存在。基本上 NSOperationQueue 确保您通过旋转线程来处理每个委托回调来排队和处理委托回调。(在大多数情况下,当下载新数据或连接失败、连接完成加载等时,相同的后台线程会进入睡眠状态并恢复)。一旦你有了这个队列设置委托回调将开始进入你的应用程序。

dispatch_async(connectionQueue, ^{
    connection = [[NSURLConnection alloc] initWithRequest:request delegate:self     startImmediately:NO];
    NSOperationQueue __autoreleasing *tempQueue = [[NSOperationQueue alloc] init];
    [connection setDelegateQueue:tempQueue];
    [connection start];
});

如果您选择 RunLoop,那么管理 runloop 对您来说是额外的负担。

于 2014-07-18T22:15:54.393 回答