【问题标题】:Running NSURLSession completion handler on main thread在主线程上运行 NSURLSession 完成处理程序
【发布时间】:2013-11-18 16:24:30
【问题描述】:

我正在使用 NSURLSession 来获取填充 TableView 的值。我正在更新完成处理程序中的 TableView,但使用 [[NSThread currentThread] isMainThread] 向我显示完成处理程序没有在主线程中运行。由于我应该只从主线程更新 UI,我知道这是不正确的。有没有办法从完成处理程序触发主线程上的操作?还是使用 NSURLSession 是错误的方法?

NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithURL:[NSURL URLWithString:@"http://myurl"]
        completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
            NSError *jsonError = nil;
            NSArray* jsonUsers = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
            if (jsonError) {
                NSLog(@"error is %@", [jsonError localizedDescription]);
                // Handle Error and return
                return;
            }
            self.userArray = jsonUsers;
            [self.userTableView reloadData];
            if ([[NSThread currentThread] isMainThread]){
                NSLog(@"In main thread--completion handler");
            }
            else{
                NSLog(@"Not in main thread--completion handler");
            }
}] resume];

【问题讨论】:

    标签: ios multithreading cocoa ios7


    【解决方案1】:

    是的,只需使用 GCD 调度您的主线程内容:

     NSURLSession *session = [NSURLSession sharedSession];
        [[session dataTaskWithURL:[NSURL URLWithString:@"http://myurl"]
                completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                    NSError *jsonError = nil;
                    NSArray* jsonUsers = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
                    if (jsonError) {
                        NSLog(@"error is %@", [jsonError localizedDescription]);
                        // Handle Error and return
                        return;
                    }
    
                    dispatch_async(dispatch_get_main_queue(), ^{
                        self.userArray = jsonUsers;
                        [self.userTableView reloadData];
                        if ([[NSThread currentThread] isMainThread]){
                            NSLog(@"In main thread--completion handler");
                        }
                        else{
                            NSLog(@"Not in main thread--completion handler");
                        }
                    });
    
                }] resume];
    

    【讨论】:

    • 谢谢! GDC 你说的是 Grand Central Dispatch,对吧?一般来说,我想在新块之外做尽可能多的计算吗?例如,如果我正在做一堆东西而不仅仅是self.userArray = jsonUsers;,也许我会把所有这些都放在dispatch_asyn(...之前?
    • 是的,对不起 GCD。是的,在块之前完成所有艰苦的工作将在后台线程中完成。
    • @graver 在这一行:NSURLSession *session = [NSURLSession sharedSession]; 此会话在哪个线程/队列上运行?我认为这将是主队列。但基于这个问题/答案,我想我猜错了。请告诉我这一点好吗?谢谢:)
    • @hqt 这也将是 sharedSession 的并发线程。
    【解决方案2】:

    @graver 的回答很好。这是另一种方法:

    NSURLSession* session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
                                                              delegate:nil
                                                         delegateQueue:[NSOperationQueue mainQueue]];
    [[session dataTaskWithURL:[NSURL URLWithString:@"http://myurl"]
            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                NSError *jsonError = nil;
                NSArray* jsonUsers = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
                if (jsonError) {
                    NSLog(@"error is %@", [jsonError localizedDescription]);
                    // Handle Error and return
                    return;
                }
    
                self.userArray = jsonUsers;
                [self.userTableView reloadData];
                if ([[NSThread currentThread] isMainThread]){
                    NSLog(@"In main thread--completion handler");
                }
                else{
                    NSLog(@"Not in main thread--completion handler");
                }
            }] resume];
    

    通过这种方式,您可以创建一个会话,该会话调用主线程上的完成块和任何委托方法。您可能会发现这在美学上更令人愉悦,但您确实失去了在后台运行“艰苦工作”的优势。

    【讨论】:

    • 斯威夫特:let session = NSURLSession(configuration: .defaultSessionConfiguration(), delegate: nil, delegateQueue: NSOperationQueue.mainQueue())
    • 我赞成这个答案,因为它解决了我的问题 - 非常感谢。
    【解决方案3】:

    这是从块和完成处理程序更新 UI 的最佳方式,当您不确认哪个线程运行您的代码时也是如此。

    static void runOnMainThread(void (^block)(void))
    {
        if (!block) return;
    
        if ( [[NSThread currentThread] isMainThread] ) {
            block();
        } else {
            dispatch_async(dispatch_get_main_queue(), block);
        }
    }
    

    这是一个静态方法,它有一个块,将在主线程上运行,它就像一个

    runOnMainThread(^{
               // do things here, it will run on main thread, like updating UI
    
            });
    

    【讨论】:

    • 现在看,实际上它是一个静态方法,它接受一个块并将在主线程上运行该代码块...您可以在您的应用程序中将此代码用作实用程序方法。
    • 嗨@C_X 我很厚你有错字。它是 [[NSThread currentThread] isMainThread],而不是 [NSThread isMainThread]。我编辑了它。随意再次编辑!
    【解决方案4】:

    你可以试试这个:

    [self.userTableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO];
    

    【讨论】:

      【解决方案5】:

      发送完成通知,表格视图控制器将观察该通知,然后执行 reloadData;

      这样做的好处是,如果您稍后将此下载代码移到单独的类中,例如模型对象,则无需更改,并且代码可以在其他项目中重用而无需更改

      【讨论】:

      • 但是,除非您采取预防措施,否则将在发布通知的同一线程上观察通知。
      【解决方案6】:

      斯威夫特 3.1

      DispatchQueue.main.async {
          tableView.reloadData()
      }
      

      【讨论】:

      • 您不需要在 DispatchQueue.main 中进行 isMainThread 检查......
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-21
      • 1970-01-01
      • 1970-01-01
      • 2014-02-25
      • 1970-01-01
      • 2015-09-10
      相关资源
      最近更新 更多