【问题标题】:iOS: NSOperationQueue: Secondary queueiOS:NSOperationQueue:辅助队列
【发布时间】:2014-02-24 10:05:42
【问题描述】:

我想以异步方式接收一些数据到服务器,并避免App UI performance 过载。因此希望将任务发送到辅助队列而不是主队列。

这是我目前使用 “主队列” 的解决方案(我理解 [NSOperationQueue mainQueue] 会降低性能):

-(NSDictionary*) fetchURL:(NSString*)url
{
    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:kCONTACTSINFOURL]];

    __block BOOL hasError = FALSE;
    __block NSDictionary *json;

    [NSURLConnection sendAsynchronousRequest:request
                                       queue:[NSOperationQueue mainQueue] 
                           completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError)
                           {
                               //Verify type of connection error

                               json = [NSJSONSerialization JSONObjectWithData:data
                                                                      options:0
                                                                        error:nil];
                               NSLog(@"Async JSON: %@", json);



                           }];

    if (hasError) {
        [[NSOperationQueue mainQueue] suspend];
        return nil;
    }
    return json;
}

为了使用二级队列,避免UI和App性能超载,可以分配一个共享的NSOperationQueue并引用它吗?或者还有其他一些“更好”的类或者实现这一点的方法?

这将是我使用辅助 NSOperationQueue 改进的解决方案:

创建辅助队列:

    NSOperationQueue* otherQueue = [NSOperationQueue init];

使用另一个(辅助)队列:

....
[NSURLConnection sendAsynchronousRequest:request
                                       queue:otherQueue
                           completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError)
....

}

这是正确的吗?或者有没有其他方法可以解决这个问题?

【问题讨论】:

  • 我认为你正在做的很好。您可能需要考虑在对象中保留队列并为每次调用使用相同的队列。或者可能将其保留为类中的静态。这可能会使以后的调试更容易。
  • 谢谢。我已将队列插入到静态类“NetworkManager”中,我可以从应用程序的任何地方访问它。这个想法是如果出现错误(例如没有可用的服务器)挂起辅助队列,将任务重新添加到辅助队列,同时触发每隔几毫秒执行一次的递归线程,这将验证可用性服务器,当服务器备份时将停止并恢复队列。这是一种明智的做法吗?
  • 不太确定你想在这里实现什么..? [NSURLConnection sendAsyn...] 在后台使用它自己的队列来发出请求。您传入的队列仅用于在连接完成时执行完成处理程序。不确定为什么要在出现错误时停止获取回调?您是否认为暂停它会停止发出请求?您是否正在尝试实现某种机制来不断了解您是否连接到服务器?
  • 是的,不仅如此。我基本上是在实现一个小型聊天应用程序。所以我需要一个队列来将消息发送到服务器。因此,出于这个原因,如果由于服务器脱机错误而没有发送消息,我想暂停消息队列并在连接备份后恢复它。任何更精确的教程/建议都会受到欢迎:-)
  • 给我几分钟,我会发布一个答案,以便我可以包含更多细节。

标签: ios multithreading nsoperation nsoperationqueue


【解决方案1】:

... 继 cmets...

NSURLConnection 方法

[NSURLConnection sendAsynchronousRequest:queue:completionHandler:]

允许您指定在连接完成时将调用完成处理程序的队列。如果您暂停此队列,您将不会停止发送请求,您只会在完成后停止接收回调。您可能想考虑稍微不同地执行此操作...

NSOperation 很棒,但我更喜欢直接使用 GCD(NSOperation 只是顶部的一个很好的 obj-c 包装器),如果你很难使用 NSOperation,请告诉我,我会添加一些建议那个。

我假设您有某种管理器类来处理您的所有服务器通信?如果没有,我建议您这样做,并将其作为单例。

@ interface ChatManager : NSObject
    + (ChatManager *)sharedManager;
@end

@implementation ChatManager {
    dispatch_queue_t fetchQueue;
    dispatch_queue_t postQueue;
}
+ (ChatManager *)sharedManager {
    // This is just the standard apple pattern for creating a singleton
    static SCAddressBookManager *sharedManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedManager = [[SCAddressBookManager alloc] init];
    });
    return sharedManager;
}

- (id)init {
    self = [super init];
    if (self) {
        // Create a dispatch queue that will run requests one after another, you could make this concurrent but that may cause messages to be lost when you suspend
        fetchQueue = dispatch_queue_create("fetch_queue", DISPATCH_QUEUE_SERIAL);
        postQueue = dispatch_queue_create("post_queue", DISPATCH_QUEUE_SERIAL);
    }
}

- (NSDictionary *)fetchURL:(NSURL *)url {
    // In here we will dispatch to our queue and so that the 
    dispatch_async(fetchQueue, ^{
        NSURLRequest = // create your request
        NSURLResponse *response = nil;
        NSError *error = nil;
        NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
        // Check for error
        if (error) dispatch_suspend(fetchQueue); // this suspends the queue that is making the calls to the server, so will stop attempting to send messages when you have an error - you should start pinging to see when you come back online here too, and then use dispatch_resume(fetchQueue) to get it going again!
        else {
            json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
            NSLog(@"Async JSON: %@", json);
            return json; // ??
        }
    });
}

- (NSDictionary *)postURL:(NSURL *)url data:(NSData *)bodyData {
    // In here we will dispatch to our queue and so that the 
    dispatch_async(postQueue, ^{
        /*
          Make your post request.
        */
        // Check for error
        if (error) dispatch_suspend(postQueue);
        else {
            json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
            NSLog(@"Async JSON: %@", json);
            return json; // ??
        }
    });
}

@end

您可能还需要使用

dispatch_sync(dispatch_get_main_queue(), ^{
    // Do stuff on the main thread here
};

如果您需要对主线程进行委托样式回调!

这是关于我将如何进行设置以尝试实现您的目标的基本想法...如果您觉得其中任何一个没有意义,请随时告诉我..?

作为旁注,我假设无论出于何种原因,您的服务器都需要一个简单的 http 接口。更好的方法是在应用程序和您的服务器之间打开一个持久套接字,然后您可以随意上下推送数据。带有心跳的套接字也会让您知道您的连接何时断开。不确定您是否希望我进一步详细说明此选项...

【讨论】:

  • 我不同意。 NSOperationQueue 更简单,更高级。我认为它是比 GCD 更好的选择,除非您有非常具体的要求,但 OP 没有提及。
  • @MichałCiuba 我同意你的说法,我只是碰巧更熟悉/更熟悉 GCD,所以更容易写出答案:)
  • 非常感谢!是否可以有多个调度队列?一个用于 GET/FETCH,一个用于 POST?
  • 当然,只有两个 ivars 和一个用于获取/获取和发布的单独方法。在合理的范围内,您可以拥有任意数量!
  • 刚刚做了一个编辑,我还添加了如果你需要的话如何回调到主线程。
猜你喜欢
  • 2016-05-02
  • 1970-01-01
  • 1970-01-01
  • 2015-10-12
  • 1970-01-01
  • 2012-09-18
  • 1970-01-01
  • 2012-07-28
  • 2012-10-10
相关资源
最近更新 更多