从异步回调更新UI的问题

时间:2016-01-08 13:45:01

标签: ios objective-c multithreading

我有以下代码:

@property (weak, nonatomic) IBOutlet UIView *loadingView;

-(void) hideLoadingScreen{
    self.loadingViewRect = self.loadingView.frame;
    [self.loadingView setHidden:YES];
    [self.loadingView setFrame:CGRectMake(0,0,0,0)];
}
- (void)viewDidAppear:(BOOL)animated{
    [self.apiClient checkLoggedInWithCallback:^(int status){
        if(status == ONLINE_LOGGED_IN){
            [self.dbAPI isPullSynchronized:^(BOOL isPullSynced){
                if(isPullSynced){
                    self.data = [self.dbAPI getTodayVisits];
                    [[self tableView] reloadData];
                    [self hideLoadingScreen];
                } else {
                    [self.apiClient getTodayVisits:^(NSArray* visits){
                        [self.dbAPI insertTodayVisits:visits withCallback:^(int status){
                            self.data = [self.dbAPI getTodayVisits];
                            [[self tableView] reloadData];
                        }];
                    }];
                }
            }];
        } else if(status == ONLINE_NOT_LOGGED_IN || status == ONLINE_INVALID_TOKEN || status == OFFLINE_NOT_LOGGED_IN) {
            //Redirect to login
        } else {
            //Get from Local DB
            self.data = [self.dbAPI getTodayVisits];
            [[self tableView] reloadData];
        }
    }];
}

hideLoadingScreen方法不会执行(我的意思是它会被执行,但UI不会更新)。

我尝试了所有的东西使其工作(包括通过GCD将[self hideLoadingScreen]调度到主线程并执行SelectorOnMainThread /使__block BOOL变量isLoading并在主线程上休眠直到该变量被更改等)。我还在viewDidAppear方法上调用了hideLoadingView方法并且它可以工作,但我希望它在执行回调时隐藏。不幸的是,我无法通过搜索stackoverflow找到解决方案,也没有在谷歌上找到(我必须说我尝试了所有解决方案)。

L.E。我记录了Rob Napier建议的self.loadingView

新代码:

-(void) hideLoadingScreen{
    self.loadingViewRect = self.loadingView.frame;
    NSLog(@"hideLoadingScreen before: %@",self.loadingView);
    [self.loadingView setHidden:YES];
    [self.loadingView setFrame:CGRectMake(0,0,0,0)];
    NSLog(@"hideLoadingScreen after: %@",self.loadingView);
}

- (void)viewDidAppear:(BOOL)animated{
    NSLog(@"%@",self.loadingView);
    [self.apiClient checkLoggedInWithCallback:^(int status){
        if(status == ONLINE_LOGGED_IN){
            [self.dbAPI isPullSynchronized:^(BOOL isPullSynced){
                if(isPullSynced){
                    dispatch_async(dispatch_get_main_queue(), ^{
                        self.data = [self.dbAPI getTodayVisits];
                        [[self tableView] reloadData];
                        NSLog(@"async before: %@",self.loadingView);
                        [self hideLoadingScreen];
                        NSLog(@"async after: %@",self.loadingView);
                    });
                } else {
                    [self.apiClient getTodayVisits:^(NSArray* visits){
                        [self.dbAPI insertTodayVisits:visits withCallback:^(int status){
                            self.data = [self.dbAPI getTodayVisits];
                            [[self tableView] reloadData];
                        }];
                    }];
                }
            }];
        } else if(status == ONLINE_NOT_LOGGED_IN || status == ONLINE_INVALID_TOKEN || status == OFFLINE_NOT_LOGGED_IN) {
            //Redirect to login
        } else {
            //Get from Local DB
            self.data = [self.dbAPI getTodayVisits];
            [[self tableView] reloadData];
        }
    }];
}

日志:

2016-01-08 16:22:25.973 sunwaves.reporting[4566:282042] async before: <UIView: 0x7fa681e745d0; frame = (0 0; 1024 650); autoresize = RM+H+BM; layer = <CALayer: 0x7fa681e291c0>>
2016-01-08 16:22:25.973 sunwaves.reporting[4566:282042] hideLoadingScreen before: <UIView: 0x7fa681e745d0; frame = (0 0; 1024 650); autoresize = RM+H+BM; layer = <CALayer: 0x7fa681e291c0>>
2016-01-08 16:22:25.974 sunwaves.reporting[4566:282042] hideLoadingScreen after: <UIView: 0x7fa681e745d0; frame = (0 0; 0 0); hidden = YES; autoresize = RM+H+BM; layer = <CALayer: 0x7fa681e291c0>>
2016-01-08 16:22:25.974 sunwaves.reporting[4566:282042] async after: <UIView: 0x7fa681e745d0; frame = (0 0; 0 0); hidden = YES; autoresize = RM+H+BM; layer = <CALayer: 0x7fa681e291c0>>

1 个答案:

答案 0 :(得分:2)

大多数UIKit调用必须在主队列上进行。您应该使用dispatch_async(dispatch_get_main_queue(),...来执行此操作。

这包括您至少拨打reloadData的电话。

您对self.data的分配也可能不是线程安全的(除非您在setter中做了一些特殊的事情)。所以那些需要在主队列上。

当然,您拨打hideLoadingScreen

我假设大多数这些块都在主队列中执行,因此这意味着在几个地方放入dispatch_async

- (void)viewDidAppear:(BOOL)animated{
    [self.apiClient checkLoggedInWithCallback:^(int status){
        if(status == ONLINE_LOGGED_IN){
            [self.dbAPI isPullSynchronized:^(BOOL isPullSynced){
                if(isPullSynced){
                    dispatch_async(dispatch_get_main_queue(), ^{
                        self.data = [self.dbAPI getTodayVisits];
                        [[self tableView] reloadData];
                        [self hideLoadingScreen];
                    });
                } else {
                    [self.apiClient getTodayVisits:^(NSArray* visits){
                        [self.dbAPI insertTodayVisits:visits withCallback:^(int status){
                            dispatch_async(dispatch_get_main_queue(), ^{
                                self.data = [self.dbAPI getTodayVisits];
                                [[self tableView] reloadData];
                            });
                        }];
                    }];
                }
            }];
        } else if(status == ONLINE_NOT_LOGGED_IN || status == ONLINE_INVALID_TOKEN || status == OFFLINE_NOT_LOGGED_IN) {
            //Redirect to login
        } else {
            //Get from Local DB
            dispatch_async(dispatch_get_main_queue(), ^{
                self.data = [self.dbAPI getTodayVisits];
                [[self tableView] reloadData];
            });
        }
    }];
}