使用NSRunLoop优化界面卡的现象
1 一般UI加载是在主线程执行,避免多线程抢夺资源的情况,所以也就设置成nonatomic属性即非原子属性,那么当太多的数据需要加载的时候尤其指超高清的大图片等,这时候用户在操作比如下滑cell等就会造成很(狠)明显的UI卡顿现象,这时候使用NSRunLoop就可以优化
2 使用一个数组添加block,然后在block里面执行需要操作的任务-这里表示UI的加载
3 需要一个CFRunLoopObserverRef观察者,把VC对象传到context里面,调用VC的block执行任务
#import "JRunLoopDemo.h"
typedef void(^RunLoopBlock)();
@interface JRunLoopDemo ()<UITableViewDelegate,UITableViewDataSource>
@property (weak, nonatomic) IBOutlet UITableView *mainTabbleView;
///定义一个block
@property (strong, nonatomic) NSMutableArray *tasks;//添加任务
@property (assign, nonatomic) NSUInteger maxQueueLenth;//最大任务
@end
@implementation JRunLoopDemo
/*
UIKIt 是不安全的 因为上锁需要消耗性能
UI直接反应用户体验
苹果粑粑建议对UI操作放在主线程 不要出现抢夺资源的情况
*/
- (void)viewDidLoad {
[super viewDidLoad];
self.tasks = [NSMutableArray array];
[NSTimer scheduledTimerWithTimeInterval:0.001 repeats:YES block:^(NSTimer * _Nonnull timer) {
[self addRunLoopObserver];
}];
}
#pragma mark - UITableViewDelegate && UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return 100;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"123"];
if (!cell) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:@"123"];
}
// [self cellAddSubview:cell];
// [self cellAddSubview:cell];
// [self cellAddSubview:cell];
// [self cellAddSubview:cell];
// [self cellAddSubview:cell];
__weak typeof(self) weakSelf = self;
[self addTask:^{
[weakSelf cellAddSubview:cell];
}];
[self addTask:^{
[weakSelf cellAddSubview:cell];
}];
[self addTask:^{
[weakSelf cellAddSubview:cell];
}];
return cell;
}
- (void)cellAddSubview:(UITableViewCell *)cell{
for (int i=0;i<100; i++) {
UIView *view = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 320, 120)];
view.backgroundColor = [UIColor redColor];
[cell addSubview:view];
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 200;
}
/// 添加任务
- (void)addTask:(RunLoopBlock)task {
[self.tasks addObject:task];
if (self.tasks.count > self.maxQueueLenth) {
[self.tasks removeObjectAtIndex:0];
}
}
/// 添加观察者
- (void)addRunLoopObserver{
// 拿到当前的RunLoop
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
//上下文
CFRunLoopObserverContext context = {
0,
(__bridge void*)(self),
&CFRetain,
&CFRelease,
NULL
};
// 定义一个观察者
static CFRunLoopObserverRef defautModelObserver;
// 创建一个观察者
defautModelObserver = CFRunLoopObserverCreate(NULL,
kCFRunLoopBeforeWaiting, YES, 0, &Callback, &context);
// 添加观察者
CFRunLoopAddObserver(runLoop, defautModelObserver, kCFRunLoopCommonModes);
CFRelease(defautModelObserver);
}
/// 定义一个函数。然后让他不断执行
static void Callback(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
JRunLoopDemo *runLoopVC = (__bridge JRunLoopDemo *)info;
if (runLoopVC.tasks.count == 0) return;
RunLoopBlock task = runLoopVC.tasks[0];
task();//执行这个任务
//执行完毕 删除任务
[runLoopVC.tasks removeObjectAtIndex:0];
}
@end
总结:核心代码是这里面的kCFRunLoopCommonModes 模式等同于NSDefaultRunLoopMode和UITrackingRunLoopMode叠加
// 添加观察者
CFRunLoopAddObserver(runLoop, defautModelObserver, kCFRunLoopDefaultMode);
另外
CFRunLoopObserverContext context = {
0,
(__bridge void*)(self),
&CFRetain,
&CFRelease,
NULL
};
把self传到Callback函数,可以在这里得到self的task() 这个block,然后直接跳用
网友评论