runloop實踐-加載高清大圖

問題描述

在用tableVIew或者scrollView加載多張高清大圖的時候,頁面會卡頓。如下圖


這里寫圖片描述

這里寫圖片描述

原因

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

解決思路

每次Runloop循環(huán),只渲染一張大圖!!

1.監(jiān)聽Runloop的循環(huán)!!
2.將加載大圖的代碼!放在一個數(shù)組里面!!
3.每次Runloop循環(huán),取出一個加載大圖的任務(wù)執(zhí)行!!

解決步驟

需要用到C語言的框架CFRunLoopRef。
FRunLoopRef 是在 CoreFoundation 框架內(nèi)的,它提供了純 C 函數(shù)的 API,所有這些 API 都是線程安全的。

NSRunLoop 是基于 CFRunLoopRef 的封裝,提供了面向?qū)ο蟮?API,但是這些 API 不是線程安全的。

CFRunLoopRef 的代碼是開源的,你可以在這里 http://opensource.apple.com/tarballs/CF/CF-855.17.tar.gz 下載到整個 CoreFoundation 的源碼。

1.監(jiān)聽Runloop的循環(huán)

在CFRunloop中可以看到

/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0),//進入runloop循環(huán)
    kCFRunLoopBeforeTimers = (1UL << 1),//處理timer之前
    kCFRunLoopBeforeSources = (1UL << 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#>   重復(fù)
     <#CFRunLoopObserverCallBack callout#> 回調(diào)的指針
     <#CFRunLoopObserverContext *context#> 上下文 傳遞參數(shù)用
     *
     /
    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調(diào)用addRunloopObserver

但是運行一次,就不執(zhí)行了。這是因為回調(diào)之后沒事情發(fā)生,所以需要定義一個timeer,此時的viewDidLoad為

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

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [NSTimer scheduledTimerWithTimeInterval:0.0001 target:self selector:@selector(timerMethod) userInfo:nil repeats:YES];
    
    
   
    
    [self addRunloopObserver];
   
}

2.將加載大圖的代碼!放在一個數(shù)組里面

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上面的子控件!! 節(jié)約內(nèi)存!!
    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循環(huán),取出一個加載大圖的任務(wù)執(zhí)行

修改callBack,callBack可以有參數(shù)
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

推薦一個第三方RunLoopWorkDistribution,地址https://github.com/diwu/RunLoopWorkDistribution
這個的原理就是利用的本文的東西

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容