获取tableView刷新完成状态

来源:互联网 发布:c语言乘法口诀 编辑:程序博客网 时间:2024/04/24 10:57


   最近有个需求,需要获取tableView的刷新完成状态去做一些事情,找了好多方法,最终在http://stackoverflow.com/questions/16071503/how-to-tell-when-uitableview-has-completed-reloaddata这里找到了答案 。

   两种方式  一种是

 [self.tableView reloadData]; [self.tableView layoutIfNeeded];
 // do sth

另外一种是
[self.tableView reloadData];dispatch_async(dispatch_get_main_queue(), ^{     // do sth});
第一种方式是刷新的时候强制tableView 立即刷新,第二种是获取主线程队列,然后当tableView刷新完成之后会调用这个GCD的block。个人比较倾向于第二种方式,于是想探究一下原理。
之前有人说[tableView reloadData}是异步的,其实并不是,下面代码验证了我的说法
我在这里

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

    static NSString * cellID = @"cell";

    TableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:cellID];

    if(!cell){

        cell = [[TableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];

    }

    NSLog(@"cell index :%ld",(long)indexPath.row);

    [cell cellRelaodData];

    return cell;

}

打印每个cell,然后调用

- (void)viewDidLoad {

NSLog(@"tableView reloadData start");

    [_tableView reloadData];//

    sleep(5);

}

[_tableView reloadData]如果说reload是异步的,那么即使主线程阻塞,它也会执行刷新操作,我们来看打印结果:

2015-12-04 10:40:15.524 RunLoop[17929:2244708] tableView reloadData start

2015-12-04 10:40:20.577 RunLoop[17929:2244708] cell index :0

2015-12-04 10:40:20.615 RunLoop[17929:2244708] cell index :1

2015-12-04 10:40:20.616 RunLoop[17929:2244708] cell index :2

tableView是在5秒之后才刷新的,那么它一定是同步的!!

那既然是同步的,他又是什么时候执行的呢?

- (void)viewDidLoad {

  NSLog(@"tableView reloadData start");

  [_tableView reloadData];//

  

  [self test];

}

test函数里面打印了一句话,我们再来看log

2015-12-04 10:43:24.408 RunLoop[17970:2250279] tableView reloadData start

2015-12-04 10:43:24.409 RunLoop[17970:2250279] the test method is end

2015-12-04 10:43:24.434 RunLoop[17970:2250279] cell index :0

2015-12-04 10:43:24.440 RunLoop[17970:2250279] cell index :1

2015-12-04 10:43:24.441 RunLoop[17970:2250279] cell index :2

是在tableView reloadData操作所在的函数执行完毕之后才执行的。。我们继续改代码

- (void)viewDidLoad {

  NSLog(@"tableView reloadData start");

  [_tableView reloadData];//

  [self test];

  dispatch_async(dispatch_get_main_queue(), ^{ //2

    NSLog(@"tableView reloadData complete");

  });

  NSLog(@"the last line of current selector");

  

}

再看log

2015-12-0410:45:54.588 RunLoop[18009:2255040] tableView reloadData start

2015-12-0410:45:54.589 RunLoop[18009:2255040] the test method is end

2015-12-0410:45:54.589 RunLoop[18009:2255040] the last line of current selector

2015-12-0410:45:54.615 RunLoop[18009:2255040] cell index :0

2015-12-0410:45:54.620 RunLoop[18009:2255040] cell index :1

2015-12-0410:45:54.621 RunLoop[18009:2255040] cell index :2

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

2015-12-0410:45:54.665 RunLoop[18009:2255040] cell index :96

2015-12-0410:45:54.666 RunLoop[18009:2255040] cell index :97

2015-12-0410:45:54.666 RunLoop[18009:2255040] cell index :98

2015-12-0410:45:54.666 RunLoop[18009:2255040] cell index :99

2015-12-0410:45:54.916 RunLoop[18009:2255040] tableView reloadData complete


执行顺序是viewdidLoad->test->table ralodData->

dispatch_async(dispatch_get_main_queue(), ^{}block


接下来我们添加一个runloop的observer

- (void)runLoopObsevers{

  

    // 创建RunLoop即将唤醒监听者

    CFRunLoopObserverRef observer =CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(),kCFRunLoopAllActivities,YES,0, ^(CFRunLoopObserverRef observer,CFRunLoopActivity activity) {

        

        switch (activity) {

            casekCFRunLoopEntry:

                NSLog(@"即将进入Loop");

                break;

            casekCFRunLoopBeforeTimers:

                NSLog(@"即将处理 Timer");

                break;

            casekCFRunLoopBeforeSources:

                NSLog(@"即将处理 Source");

                break;

            casekCFRunLoopBeforeWaiting:

                NSLog(@"即将进入休眠");

                break;

            casekCFRunLoopAfterWaiting:

                NSLog(@"刚从休眠中唤醒");

                break;

            case kCFRunLoopExit:

                NSLog(@"即将退出Loop");

                break;

            default:

                break;

        }

        

        

    });

    // 向当前runloop添加监听者

    CFRunLoopAddObserver(CFRunLoopGetMain(), observer,kCFRunLoopDefaultMode);

    // 释放内存

    CFRelease(observer);


}


在viewdidload里面调用

[selfrunLoopObsevers];

再来看log

2015-12-04 11:10:35.241 RunLoop[1107:54022] cell index :2

2015-12-04 11:10:35.242 RunLoop[1107:54022] cell index :3

2015-12-04 11:10:35.243 RunLoop[1107:54022] cell index :4

2015-12-04 11:10:35.243 RunLoop[1107:54022] cell index :5

2015-12-04 11:10:35.244 RunLoop[1107:54022] cell index :6

2015-12-04 11:10:35.245 RunLoop[1107:54022] cell index :7

2015-12-04 11:10:35.492 RunLoop[1107:54022]即将进入Loop

2015-12-04 11:10:35.493 RunLoop[1107:54022]即将处理 Timer

2015-12-29 11:10:35.493 RunLoop[1107:54022] 即将处理 Source

2015-12-29 11:10:35.494 RunLoop[1107:54022] tableView reloadData complete

2015-12-29 11:10:35.494 RunLoop[1107:54022] 即将处理 Timer

2015-12-29 11:10:35.495 RunLoop[1107:54022] 即将处理 Source


可以看到处理dispatch_get_main_queue的block任务的时候已经进入下一次runloop的迭代了


runloop优先处理了tableview reloadData的任务,之后进入休眠,被dispatch_get_main_queue的任务唤醒它的block只有在当前的主线程空闲的时候才有机会被runloop处理,所以最终这种方式可以获取到tableView的刷新完成状态.

最后献上完整版:

dispatch_async(dispatch_get_main_queue(), ^{ 
  [self.tableView reloadData];  dispatch_async(dispatch_get_main_queue(), ^{     // do sth  });
});

外层的dispatch_async(dispatch_get_main_queue(), ^{})是为了保证调用reloadData函数的时候其他的工作已经做完了。


               

runloop的参考资料在这里

http://www.cocoachina.com/ios/20150601/11970.html


https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html#//apple_ref/doc/uid/10000057i-CH16-SW1

               

以上是个人理解,欢迎大家讨论指正。


1 0
原创粉丝点击