亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

iOS中的UITableView的重用機制與加載優(yōu)化詳解

 更新時間:2017年02月09日 08:38:14   作者:程序員小強  
本篇文章主要介紹了iOS中的UITableView的重用機制與加載優(yōu)化詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

UITableView可以說是UIKit中最重要的一個組件,用來展示數(shù)據(jù)列表,還可以靈活使用進行頁面的布局。UITableView的使用遵循MVC模式,數(shù)據(jù)模型(NSObject)、視圖(UIView)和控制器(UITableViewController)分離。UITableView繼承自UIScrollView,可上下滑動,可以作為跟視圖也可以作為子視圖組件。

reuseIdentifier顧名思義是一個復(fù)用標(biāo)識符,是一個自定義的獨一無二的字符串,用來唯一地標(biāo)記某種重復(fù)樣式的可復(fù)用UITableViewCell,系統(tǒng)是通過reuseIdentifier來確定已經(jīng)創(chuàng)建了的指定樣式的cell來進行復(fù)用,iOS中表格的cell通過復(fù)用來提高加載效率,因為多數(shù)情況下表格中的cell樣式都是重復(fù)的,只是數(shù)據(jù)模型不同而已,因此系統(tǒng)可以在保證創(chuàng)建足夠數(shù)量的cell鋪滿屏幕的前提下,通過保存并重復(fù)使用已經(jīng)創(chuàng)建的cell來提高加載效率和優(yōu)化內(nèi)存,避免不停地創(chuàng)建和銷毀cell元素。

UITableViewCell的復(fù)用原理其實很簡單,可以通過下面一個簡單的例子來理解:

首先在開發(fā)中我們在UITableViewController類中寫cell復(fù)用代碼的最基本模板會像下面這樣:

/**
 * 可復(fù)用cell制作
 */
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  // 定義cell重用的靜態(tài)標(biāo)志符
  static NSString *cell_id = @"cell_id_demo";
  // 優(yōu)先使用可復(fù)用的cell
  UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cell_id];
  // 如果要復(fù)用的cell還沒有創(chuàng)建,則創(chuàng)建一個供之后復(fù)用
  if (cell == nil) {
    // 新創(chuàng)建cell并使用cell_id復(fù)用符標(biāo)記
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cell_id];
  }
  // 配置cell數(shù)據(jù)
  cell.textLabel.text = [NSString stringWithFormat:@"Cell%i", countNumber];
  // 其他cell設(shè)置...
  return cell;
}

代碼這樣寫的原因是通過調(diào)用當(dāng)前tableView的dequeueReusableCellWithIdentifier方法看指定的reuseIdentifier是否有可以重復(fù)使用的了,如果有則會返回可復(fù)用的cell,cell就緒之后便可以開始更新cell的數(shù)據(jù);如果還不可復(fù)用,則返回nil,然后會進入后面的if語句,此時創(chuàng)建新的cell并對其設(shè)置cell樣式標(biāo)記reuseIdentifier。注意上面的if語句并不是只要執(zhí)行一次創(chuàng)建一次新的cell就完成任務(wù),然后之后全部重復(fù)利用新創(chuàng)建的那一個cell,這是對cell復(fù)用機制的誤解。

事實是要創(chuàng)建足夠數(shù)量的可覆蓋整個tableView的可復(fù)用cell之后才會開始復(fù)用之前的(UITableView中有一個visiableCells數(shù)組保存當(dāng)前屏幕可見的cell,還有一個reusableTableCells數(shù)組用來保存那些可復(fù)用的cell),這個我們用下面的測試來驗證。

如何簡潔清楚的展示UITableViewCell的復(fù)用機制呢?這里的方法是創(chuàng)建最基本的文本cell,并創(chuàng)建一個cell創(chuàng)建計數(shù)器,每次新創(chuàng)建cell計數(shù)器加1并顯示在cell上,如果是復(fù)用的cell則會顯示是復(fù)用的哪一個cell,測試代碼如下:

/**
 * 分區(qū)個數(shù)設(shè)置為1
 */
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
  return 1;
}

/**
 * 創(chuàng)建20個cell,保證覆蓋并超出整個tableView
 */
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  return 20;
}

/**
 * cell復(fù)用機制測試
 */
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  // 定義cell重用的靜態(tài)標(biāo)志符
  static NSString *cell_id = @"cell_id_demo";
  // 計數(shù)用
  static int countNumber = 1;
  // 優(yōu)先使用可復(fù)用的cell
  UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cell_id];
  // 如果要復(fù)用的cell還沒有創(chuàng)建,則創(chuàng)建一個供之后復(fù)用
  if (cell == nil) {
    // 新創(chuàng)建cell并使用cell_id復(fù)用符標(biāo)記
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cell_id];
    // 計數(shù)器標(biāo)記新創(chuàng)建的cell
    cell.textLabel.text = [NSString stringWithFormat:@"Cell%i", countNumber];
    // 計數(shù)器遞增
    countNumber++;
  }
  return cell;
}

運行在iPhone5S設(shè)備上(UITableViewController作為跟控制器,tableView覆蓋整個屏幕),20個cell顯示結(jié)果依次為:

Cell1、Cell2、Cell3、Cell4、Cell5、Cell6、Cell7、Cell8、Cell9、Cell10、Cell11、Cell12、Cell13、Cell14、Cell1、Cell2、Cell3、Cell4、Cell5、Cell6

這里寫圖片描述

這里寫圖片描述

可以看出一共創(chuàng)建了14個cell,其中整個屏幕可顯示13個cell,系統(tǒng)多創(chuàng)建一個的原因是保證在表格滑動顯示半個cell時仍然能覆蓋整個tableView。之后的6個cell就是復(fù)用了開始創(chuàng)建的那6個cell了。這樣UITableViewCell復(fù)用的基本機制就很清楚了,另外還會有reloadData或者reloadRowsAtIndex等刷新表格數(shù)據(jù)的情況,可能會伴隨新的cell創(chuàng)建和可復(fù)用cell的更新,但也是建立在基本復(fù)用機制的基礎(chǔ)之上的。

能否在一個視圖控制器中嵌入兩個tableview控制器?

可以,相當(dāng)于視圖以及視圖控制器的嵌套,視圖可以添加子視圖,視圖控制器也可以添加子控制器。這么問應(yīng)該是因為這種情況有時會用到而且很重要,因為有一點容易被忽視,就是將子視圖添加到了父視圖卻忘記將對應(yīng)的控制器作為子控制器添加到父控制器,導(dǎo)致子視圖能顯示但是不能響應(yīng)(沒有對接好控制器)。例如在當(dāng)前視圖上放一個小尺寸的表格組件,也就是在UIViewController上添加一個UITableViewController子控制器及其子view:

  // 假設(shè)有三個視圖控制器,一個作為父控制器,兩個作為子控制器
  UIViewController *superVC = [[UIViewController alloc]init];
  UITableViewController *subVC1 = [[UITableViewController alloc]init];
  UITableViewController *subVC2 = [[UITableViewController alloc]init];

  // 將子視圖控制器添加到父視圖控制器(要注意調(diào)整子視圖的尺寸和位置合理顯示,這里忽略)
  [superVC.view addSubview:subVC1.view];
  [superVC addChildViewController:subVC1];

  [superVC.view addSubview:subVC2.view];
  [superVC addChildViewController:subVC2];

  // 子視圖控制器的移除有對稱的方法,但只能是子視圖控制器主動從父視圖控制器中移除
  [subVC1.view removeFromSuperview];
  [subVC1 removeFromParentViewController];

  [subVC2.view removeFromSuperview];
  [subVC2 removeFromParentViewController];

此外要注意和presentViewController函數(shù)添加子視圖控制器的區(qū)別,上面手動添加子視圖控制器是可以自由調(diào)整子視圖的frame的(包括子視圖位置和尺寸),而presentViewController是用于頁面切換,切換后的子頁面會覆蓋整個屏幕而不可以自由調(diào)整子頁面位置和尺寸,對稱的子視圖控制器移除方法為dismissViewControllerAnimated:

  // 顯示子視圖控制器,completion后的代碼塊如果不為空添加結(jié)束后會觸發(fā)
  [[parentVC presentViewController:childVC animated:NO completion:nil];
  // 移除子視圖控制器,completion后的代碼塊如果不為空添加結(jié)束后會觸發(fā)
  [childVC dismissViewControllerAnimated:NO completion:nil];

一個tableView是否可以關(guān)聯(lián)兩個不同的datasource數(shù)據(jù)源?如何處理?

多個數(shù)據(jù)源是完全可以的,關(guān)鍵是如何關(guān)聯(lián),問題的重點是如何處理,因為將數(shù)據(jù)源(Model)和tableview視圖(View)的對接工作是程序員完成的,因此數(shù)據(jù)源的多少沒有根本影響。處理上可以分開依次對接,也可以通過數(shù)據(jù)的集合操作先將數(shù)據(jù)整理合并成一個數(shù)據(jù)源然后對接。

例如:一個表格中的每個cell顯示的是一個人的基本信息,為了簡單這里假設(shè)只有一個頭像和一個姓名。假設(shè)有兩個數(shù)據(jù)源,一個數(shù)據(jù)源是頭像的url數(shù)組,一個是姓名的字符串?dāng)?shù)組,對接時完全可以分開在cell數(shù)據(jù)回調(diào)中對接,也可以將兩個數(shù)組合并然后對接。

合并數(shù)據(jù)用到的數(shù)據(jù)模型:

@interface Model : NSObject

@property (nonatomic,copy) NSString *name; // 姓名
@property (nonatomic,copy) NSString *url;  // 圖片

@end

數(shù)據(jù)源緩沖器:

// 數(shù)據(jù)源
@property (nonatomic, strong)NSArray *name_datasource;
@property (nonatomic, strong)NSArray *url_datasource;
@property (nonatomic, strong)NSMutableArray *datasource;

處理多數(shù)據(jù)源:

/**
 * 請求數(shù)據(jù)
 */
- (void)request {
  // 姓名數(shù)據(jù)源
  _name_datasource = @[@"張三", @"李四", @"小明", @"小李"];
  _url_datasource = @[@"male", @"male", @"male", @"male"];

  // 合并數(shù)據(jù)源
  for (int i; i<_name_datasource.count; i++) {
    Model *model = [[Model alloc]init];
    model.name = _name_datasource[i];
    model.url = _url_datasource[i];
    [_datasource addObject:model];
  }
}

數(shù)據(jù)對接:

/**
 * cell數(shù)據(jù)回調(diào)
 */
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

  static NSString *identifier = @"identifier";
  // 自制cell組件
  AccountCell *cell = [[AccountCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];

  /** 多數(shù)據(jù)源分開對接:**/
  // 頭像
  [cell.avatar setImage:[UIImage imageNamed:_url_datasource[indexPath.row]]];
  // 姓名
  cell.name.text = _name_datasource[indexPath.row];

  // 或者:

  /** 數(shù)據(jù)源合并后對接**/
  // 取出對應(yīng)數(shù)據(jù)模型
  Model *model = _datasource[indexPath.row];
  // 頭像
  [cell.avatar setImage:[UIImage imageNamed:model.url]];
  // 姓名
  cell.name.text = model.name;

  return cell;
}

如何對UITableView的滾動加載進行優(yōu)化,防止卡頓?

UITableView的滾動優(yōu)化主要在于以下兩個方面:

  • 減少cellForRowAtIndexPath代理中的計算量(cell的內(nèi)容計算)
  • 減少heightForRowAtIndexPath代理中的計算量(cell的高度計算)

這里寫圖片描述

減少cellForRowAtIndexPath代理中的計算量

  • 首先要提前計算每個cell中需要的一些基本數(shù)據(jù),代理調(diào)用的時候直接取出;
  • 圖片要異步加載,加載完成后再根據(jù)cell內(nèi)部UIImageView的引用設(shè)置圖片;
  • 圖片數(shù)量多時,圖片的尺寸要跟據(jù)需要提前經(jīng)過transform矩陣變換壓縮好(直接設(shè)置圖片的contentMode讓其自行壓縮仍然會影響滾動效率),必要的時候要準(zhǔn)備好預(yù)覽圖和高清圖,需要時再加載高清圖。
  • 圖片的‘懶加載'方法,即延遲加載,當(dāng)滾動速度很快時避免頻繁請求服務(wù)器數(shù)據(jù)。
  • 盡量手動Drawing視圖提升流暢性,而不是直接子類化UITableViewCell,然后覆蓋drawRect方法,因為cell中不是只有一個contentview。繪制cell不建議使用UIView,建議使用CALayer。原因要參考UIView和CALayer的區(qū)別和聯(lián)系。

減少heightForRowAtIndexPath代理中的計算量

  • 由于每次TableView進行update更新都會對每一個cell調(diào)用heightForRowAtIndexPath代理取得最新的height,會大大增加計算時間。如果表格的所有cell高度都是固定的,那么去掉heightForRowAtIndexPath代理,直接設(shè)置TableView的rowHeight屬性為固定的高度;
  • 如果高度不固定,應(yīng)盡量將cell的高度數(shù)據(jù)計算好并儲存起來,代理調(diào)用的時候直接取,即將height的計算時間復(fù)雜度降到O(1)。例如:在異步請求服務(wù)器數(shù)據(jù)時,提前將cell高度計算好并作為dataSource的一個數(shù)據(jù)存到數(shù)據(jù)庫供隨時取用。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評論