iOS開發(fā)中,UITableView的優(yōu)化一直是一個老生常談的問題,除了常用的預(yù)加載础嫡,緩存等方法以外,其實(shí)利用RunLoop 的循環(huán)也可以實(shí)現(xiàn)超清大圖的流暢加載酝惧,具體的使用方法我們利用一個demo來詳細(xì)理解:
首先榴鼎,我們有一個需求,就是要從網(wǎng)絡(luò)加載高清大圖到UITableViewCell上晚唇,而且每個Cell上面加載多張圖片,當(dāng)cell數(shù)量過多的時候哩陕,我們需要保持流暢度和加載速度平项。
那么我們做一個簡單的分析:
1,因?yàn)檫@里用到了Runloop循環(huán),那么我們可以監(jiān)聽到runloop的每次循環(huán),
在每一次循環(huán)當(dāng)中我們考慮去進(jìn)行一次圖片下載和布局悍及。
2,既然要在每次循環(huán)執(zhí)行一次任務(wù)闽瓢,
我們可以先把所有圖片加載的任務(wù)代碼塊添加到一個數(shù)組當(dāng)中,
每次循環(huán)取出第一個任務(wù)進(jìn)行執(zhí)行心赶。*
3,因?yàn)閞unloop在閑置的時候會自動休眠鸳粉,
所以我們要想辦法讓runloop始終處于循環(huán)中的狀態(tài)。
好的园担,那么我們就可以開始考慮代碼實(shí)現(xiàn):
第一步届谈,先把uitableview基本效果實(shí)現(xiàn)
@property (strong,nonatomic) UITableView* showImageTableView;
//懶加載
-(UITableView *)showImageTableView{
if (!_showImageTableView) {
_showImageTableView = [[UITableView alloc]initWithFrame:[UIScreen mainScreen].bounds style:UITableViewStylePlain];
_showImageTableView.backgroundColor = [UIColor yellowColor];
_showImageTableView.delegate = self;
_showImageTableView.dataSource = self;
}
return _showImageTableView;
}
//注冊
[self.showImageTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:ShowImageTableViewReusableIdentifier];
//添加
[self.view addSubview:self.showImageTableView];
//數(shù)據(jù)源代理
#pragma mark- UITableViewDelegate
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:ShowImageTableViewReusableIdentifier];
//每個cell中添加3張圖片
for (int i = 0; i < 3; i++)
{
#這里是添加圖片的方法
}
return cell;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return 399;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return 135;
}
第二步,初始化可變數(shù)組用來存儲任務(wù)(代碼塊)
typedef void(^SaveFuncBlock)();
//存放任務(wù)的數(shù)組
@property (nonatomic, strong) NSMutableArray *saveTaskMarr;
//最大任務(wù)數(shù)(超過最大任務(wù)數(shù)的任務(wù)就停止執(zhí)行)
@property (nonatomic, assign) NSInteger maxTasksNumber;
//任務(wù)執(zhí)行的代碼塊
@property (nonatomic, copy) SaveFuncBlock saveFuncBlock;
-(NSMutableArray *)saveTaskMarr{
if (!_saveTaskMarr) {
_saveTaskMarr = [NSMutableArray array];
}
return _saveTaskMarr;
}
self.maxTasksNumber = 18;
第三步弯汰,新建cell添加圖片的方法
-(void)addImageToCell:(UITableViewCell*)cell andTag:(NSInteger)tag{
UIImageView* cellImageView = [[UIImageView alloc]initWithFrame:CGRectMake(tag*(ImageWidth+5), 5, ImageWidth, ImageHeight)];
dispatch_async(dispatch_get_global_queue(0,0), ^{
NSData* imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://img5.duitang.com/uploads/item/201312/14/20131214173346_iVKdT.jpeg"]];
dispatch_async(dispatch_get_main_queue(), ^{
cellImageView.image = [UIImage imageWithData:imageData];
[cell.contentView addSubview:cellImageView];
});
});
}
第四步艰山,將任務(wù)添加到數(shù)組保存
//添加任務(wù)進(jìn)數(shù)組保存
-(void)addTasks:(SaveFuncBlock)taskBlock{
[self.saveTaskMarr addObject:taskBlock];
//超過每次最多執(zhí)行的任務(wù)數(shù)就移出當(dāng)前數(shù)組
if (self.saveTaskMarr.count > self.maxTasksNumber) {
[self.saveTaskMarr removeObjectAtIndex:0];
}
}
第五步,在cellForRow方法當(dāng)中咏闪,添加方法:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:ShowImageTableViewReusableIdentifier];
for (int i = 0; i < 3; i++)
{
//添加任務(wù)到數(shù)組
__weak typeof(self) weakSelf = self;
[self addTasks:^{
//下載圖片的任務(wù)
[weakSelf addImageToCell:cell andTag:i];
}];
}
return cell;
}
第六步曙搬,監(jiān)聽RunLoop
//注冊監(jiān)聽
[self addRunloopObserver];
//這里面都是C語言 -- 添加一個監(jiān)聽者
-(void)addRunloopObserver{
//獲取當(dāng)前的RunLoop
CFRunLoopRef runloop = CFRunLoopGetCurrent();
//定義一個centext
CFRunLoopObserverContext context = {
0,
( __bridge void *)(self),
&CFRetain,
&CFRelease,
NULL
};
//定義一個觀察者
static CFRunLoopObserverRef defaultModeObsever;
//創(chuàng)建觀察者
defaultModeObsever = CFRunLoopObserverCreate(NULL,
kCFRunLoopBeforeWaiting,
YES,
NSIntegerMax - 999,
&Callback,
&context
);
//添加當(dāng)前RunLoop的觀察者
CFRunLoopAddObserver(runloop, defaultModeObsever, kCFRunLoopDefaultMode);
//c語言有creat 就需要release
CFRelease(defaultModeObsever);
}
第七步,也是最關(guān)鍵的步驟,使用定時器纵装,保持RunLoop循環(huán)中征讲。
//定時器,保證runloop一直處于循環(huán)中
@property (nonatomic, weak) NSTimer *timer;
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.001 target:self selector:@selector(setRunLoop) userInfo:nil repeats:YES];
//此方法主要是利用計(jì)時器事件保持runloop處于循環(huán)中橡娄,不用做任何處理
-(void)setRunLoop{}
最后一步诗箍,在runLoop循環(huán)中去處理事件
//MARK: 回調(diào)函數(shù)
//定義一個回調(diào)函數(shù) 一次RunLoop來一次
static void Callback(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
ViewController * vcSelf = (__bridge ViewController *)(info);
if (vcSelf.saveTaskMarr.count > 0) {
//獲取一次數(shù)組里面的任務(wù)并執(zhí)行
SaveFuncBlock funcBlock = vcSelf.saveTaskMarr.firstObject;
funcBlock();
[vcSelf.saveTaskMarr removeObjectAtIndex:0];
}
}
好啦。到此為止挽唉,我們想要的效果就基本出來了滤祖,大家可以去試試??,同樣的道理也可以應(yīng)用于其他場景瓶籽。