1.runloop在执行任务时循环速度很快,一秒钟要循环多次,当发现没有内核mach_msg事件后,进入睡眠,每隔一分钟循环一次。
2.监听reloadData完成
先来看看执行reloadData都发生了什么。
reloadData是一个异步方法,更具体点来说,是reloadData这个调用中包含了异步操作。
当我们reloadData的时候,我们本意是刷新UITableView,随后会进入一系列UITableViewDataSource和UITableViewDelegate的回调,其中有些是和reloadData同步发生的,有些则是异步发生的。
- 同步:
tableView:numberOfRowsInSection:
- 异步:
tableView:cellForRowAtIndexPath:
-
tableView:heightForRowAtIndexPath:
这个方法有些特殊,在同步和异步的时候都进行了回调。
一次runloop有两个机会执行GCD dispatch main queue中的任务,分别在休眠前和被唤醒后。设置tableView layoutIfNeeded为YES,在即将进入休眠时执行异步任务,重绘一次界面。
所以监听reloadData完成可以有两种方法
方法一:唤醒时刻当作reloadData完成
dispatch_async(dispatch_get_main_queue(), ^{
_isReloadDone = NO;
[tableView reload]; //会自动设置tableView layoutIfNeeded为YES,意味着将会在runloop结束时重绘table
dispatch_async(dispatch_get_main_queue(),^{
_isReloadDone = YES;
});
});
方法二:自定义UITableView,layoutSubviews之后当作reloadData完成(复杂,但可以更好的理解方法一)
#import "DBTableView.h"
@interface DBTableView()
@property (nonatomic, copy) void (^reloadDataCompletionBlock)();
@end
@implementation DBTableView
- (void)reloadDataWithCompletion:(void (^)())completionBlock {
self.reloadDataCompletionBlock = completionBlock;
[super reloadData];
}
- (void)layoutSubviews {
[super layoutSubviews];
if (self.reloadDataCompletionBlock) {
self.reloadDataCompletionBlock();
self.reloadDataCompletionBlock = nil;
}
}
@end
调用的时候
[self.tableView reloadDataWithCompletion:^{
NSLog(@"完成刷新");
}];
在主线程中执行dispatch_async(dispatch_get_main_queue()
做了什么?
通常我们在子线程任务结束后使用dispatch_async(dispatch_get_main_queue()
来回到主线程刷新UI,那么在主线程使用它是为了什么呢?
情景:在快速滑动的时候reload
dispatch_async(dispatch_get_main_queue(), ^{
[tableView reloadData];
});
加入主队列后,会在合适的一次loop循环中进行reload操作,并且在这次loop中渲染cell,实现流畅的滑动。
而没有加入主队列的reload,渲染cell的时机不固定。一般发生跳屏、白屏,就是因为需要等到tableView停下才会继续渲染cell。
参考链接:
iOS 事件处理机制与图像渲染过程