在主线程上运行NSURLSession完成处理程序

时间:2021-07-09 00:39:53

I am using a NSURLSession to get the values to populate a TableView. I am updating the TableView in the completion handler, but using [[NSThread currentThread] isMainThread] has shown me that the completion handler isn't running in the main thread. Since I should only updating the UI from the main thread, I know this isn't correct. Is there a way to trigger an action on the main thread from the completion handler? Or is using a NSURLSession the wrong way to go about this?

我使用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];

6 个解决方案

#1


38  

Yes, just dispatch your main thread stuff using GCD:

是的,只需使用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];

#2


15  

@graver's answer is good. Here's another way you can do it:

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

This way you create a session that calls the completion block and any delegate methods on the main thread. You may find this more aesthetically pleasing, but you do lose the advantage of running the "hard work" in the background.

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

#3


3  

You can try this:

你可以试试这个:

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

#4


3  

Here is the best way to update UI from blocks and completion handler, and also when you not confrim which thread running your code.

这是从块和完成处理程序更新UI的最佳方法,也是在您没有确定运行代码的线程时。

static void runOnMainThread(void (^block)(void))
{
    if (!block) return;

    if ( [[NSThread currentThread] isMainThread] ) {
        block();
    } else {
        dispatch_async(dispatch_get_main_queue(), block);
    }
}

This is static method which will have a block, and will run on main thread, it will act like a

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

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

        });

#5


0  

send a notification on completion that will be observed by the table view controller which will then do a reloadData;

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

this has the advantage that if you later move this download code off to a separate class, e.g. a model object, then no changes are required and also the code can be reused in other projects without making changes

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

#6


0  

Swift 3.1

Swift 3.1

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

#1


38  

Yes, just dispatch your main thread stuff using GCD:

是的,只需使用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];

#2


15  

@graver's answer is good. Here's another way you can do it:

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

This way you create a session that calls the completion block and any delegate methods on the main thread. You may find this more aesthetically pleasing, but you do lose the advantage of running the "hard work" in the background.

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

#3


3  

You can try this:

你可以试试这个:

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

#4


3  

Here is the best way to update UI from blocks and completion handler, and also when you not confrim which thread running your code.

这是从块和完成处理程序更新UI的最佳方法,也是在您没有确定运行代码的线程时。

static void runOnMainThread(void (^block)(void))
{
    if (!block) return;

    if ( [[NSThread currentThread] isMainThread] ) {
        block();
    } else {
        dispatch_async(dispatch_get_main_queue(), block);
    }
}

This is static method which will have a block, and will run on main thread, it will act like a

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

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

        });

#5


0  

send a notification on completion that will be observed by the table view controller which will then do a reloadData;

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

this has the advantage that if you later move this download code off to a separate class, e.g. a model object, then no changes are required and also the code can be reused in other projects without making changes

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

#6


0  

Swift 3.1

Swift 3.1

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