1. 程式人生 > >runloop實戰應用——載入高清大圖

runloop實戰應用——載入高清大圖

問題描述

在用tableVIew或者scrollView載入多張高清大圖的時候,頁面會卡頓。如下圖
這裡寫圖片描述
這裡寫圖片描述

原因

runloop在一次渲染中,需要渲染十幾張高清大圖,所以卡主了

解決思路

每次Runloop迴圈,只渲染一張大圖!!

1.監聽Runloop的迴圈!!
2.將載入大圖的程式碼!放在一個數組裡面!!
3.每次Runloop迴圈,取出一個載入大圖的任務執行!!

解決步驟

需要用到C語言的框架CFRunLoopRef。
FRunLoopRef 是在 CoreFoundation 框架內的,它提供了純 C 函式的 API,所有這些 API 都是執行緒安全的。

NSRunLoop 是基於 CFRunLoopRef 的封裝,提供了面向物件的 API,但是這些 API 不是執行緒安全的。

1.監聽Runloop的迴圈

在CFRunloop中可以看到

/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0),//進入runloop迴圈
    kCFRunLoopBeforeTimers = (1UL << 1),//處理timer之前
    kCFRunLoopBeforeSources = (1U
L << 2),//處理Source 之前 kCFRunLoopBeforeWaiting = (1UL << 5),//runloop處理完任何事情,都會等待,這是在處理完後等待前 kCFRunLoopAfterWaiting = (1UL << 6),//等待後 kCFRunLoopExit = (1UL << 7),//退出 kCFRunLoopAllActivities = 0x0FFFFFFFU//所有事件 };

程式碼

-(void)addRunloopObserver{
    //獲取Runloop
     CFRunLoopRef runloop = CFRunLoopGetCurrent();
    //定義一個context上下文
CFRunLoopObserverContext context = { 0, (__bridge void *)(self), &CFRetain, &CFRelease, NULL }; //定義觀察者 /** CFOptionFlags activities 就是上面的型別 <#Boolean repeats#> 重複 <#CFRunLoopObserverCallBack callout#> 回撥的指標 <#CFRunLoopObserverContext *context#> 上下文 傳遞引數用 * / static CFRunLoopObserverRef runloopObserver; runloopObserver = CFRunLoopObserverCreate(NULL, kCFRunLoopBeforeWaiting, YES, 0, &callBack, &context); //新增觀察者 CFRunLoopAddObserver(runloop, runloopObserver, kCFRunLoopCommonModes); //C裡面 一旦creat new copy CFRelease(runloopObserver); }
void callBack(){
  NSLog(@"來了");
}

然後在viewDidLoad呼叫addRunloopObserver

但是執行一次,就不執行了。這是因為回撥之後沒事情發生,所以需要定義一個timeer,此時的viewDidLoad為

-(void)timerMethod{
    //不幹任何事情!
}

- (void)viewDidLoad {
    [super viewDidLoad];

    [NSTimer scheduledTimerWithTimeInterval:0.0001 target:self selector:@selector(timerMethod) userInfo:nil repeats:YES];




    [self addRunloopObserver];

}

2.將載入大圖的程式碼!放在一個數組裡面

typedef void(^runloopBlock)(void);

-(void)addTasks:(runloopBlock)task{

    [self.tasks addObject:task];
    if (self.tasks.count > 18) {
        [self.tasks removeObjectAtIndex:0];
    }


}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:IDENTIFIER];
    cell.selectionStyle = UITableViewCellSelectionStyleNone;


    //幹掉contentView上面的子控制元件!! 節約記憶體!!
    for (NSInteger i = 1; i <= 5; i++) {
        //幹掉contentView 上面的所有子控制元件!!
        [[cell.contentView viewWithTag:i] removeFromSuperview];
    }
    //新增文字
    [ViewController addlabel:cell indexPath:indexPath];
    //新增圖片
    [self addTasks:^{
        [ViewController addImage1With:cell];
    }];

    [self addTasks:^{
        [ViewController addImage2With:cell];
    }];

    [self addTasks:^{
        [ViewController addImage3With:cell];
    }];


    return cell;
}

3.每次Runloop迴圈,取出一個載入大圖的任務執行

修改callBack,callBack可以有引數
info就是

void callBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
    ViewController * vc = (__bridge ViewController *)info;
    if(vc.tasks.count == 0){
        return;
    }
    runloopBlock block = vc.tasks.firstObject;
    block();
    [vc.tasks removeObjectAtIndex:0];


}

PS