zoukankan      html  css  js  c++  java
  • UITableView优化那点事

    forkingdog关于UITableView优化的框架其实已经能够应用在一般的场景,且有蛮多的知识点供我们借鉴,借此站在巨人的肩膀上来分析一把。

    至于UITableView的瓶颈在哪里,我相信网上随便一搜就能了解的大概,我这里顺便提供下信息点:

    高度计算

    fd_heightForCellWithIdentifier:configuration方法
     1 - (CGFloat)fd_heightForCellWithIdentifier:(NSString *)identifier configuration:(void (^)(id cell))configuration {
     2     if (!identifier) {
     3         return 0;
     4     }
     5  
     6     UITableViewCell *templateLayoutCell = [self fd_templateCellForReuseIdentifier:identifier];
     7  
     8     // Manually calls to ensure consistent behavior with actual cells. (that are displayed on screen)
     9     [templateLayoutCell prepareForReuse];
    10  
    11     // Customize and provide content for our template cell.
    12     if (configuration) {
    13         configuration(templateLayoutCell);
    14     }
    15  
    16     return [self fd_systemFittingHeightForConfiguratedCell:templateLayoutCell];
    17 }

    这里先是通过调用fd_templateCellForReuseIdentifier:从dequeueReusableCellWithIdentifier取出之后,如果需要做一些额外的计算,比如说计算cell高度, 可以手动调用 prepareForReuse方法,以确保与实际cell(显示在屏幕上)行为一致。接着执行configuration参数对Cell内容进行配置。最后通过调用fd_systemFittingHeightForConfiguratedCell:方法计算实际的高度并返回。

    fd_systemFittingHeightForConfiguratedCell方法
     1 - (CGFloat)fd_systemFittingHeightForConfiguratedCell:(UITableViewCell *)cell {
     2     CGFloat contentViewWidth = CGRectGetWidth(self.frame);
     3  
     4     // If a cell has accessory view or system accessory type, its content view's width is smaller
     5     // than cell's by some fixed values.
     6     if (cell.accessoryView) {
     7         contentViewWidth -= 16 + CGRectGetWidth(cell.accessoryView.frame);
     8     } else {
     9         static const CGFloat systemAccessoryWidths[] = {
    10             [UITableViewCellAccessoryNone] = 0,
    11             [UITableViewCellAccessoryDisclosureIndicator] = 34,
    12             [UITableViewCellAccessoryDetailDisclosureButton] = 68,
    13             [UITableViewCellAccessoryCheckmark] = 40,
    14             [UITableViewCellAccessoryDetailButton] = 48
    15         };
    16         contentViewWidth -= systemAccessoryWidths[cell.accessoryType];
    17     }
    18  
    19     // If not using auto layout, you have to override "-sizeThatFits:" to provide a fitting size by yourself.
    20     // This is the same height calculation passes used in iOS8 self-sizing cell's implementation.
    21     //
    22     // 1. Try "- systemLayoutSizeFittingSize:" first. (skip this step if 'fd_enforceFrameLayout' set to YES.)
    23     // 2. Warning once if step 1 still returns 0 when using AutoLayout
    24     // 3. Try "- sizeThatFits:" if step 1 returns 0
    25     // 4. Use a valid height or default row height (44) if not exist one
    26  
    27     CGFloat fittingHeight = 0;
    28  
    29     if (!cell.fd_enforceFrameLayout & contentViewWidth > 0) {
    30         // Add a hard width constraint to make dynamic content views (like labels) expand vertically instead
    31         // of growing horizontally, in a flow-layout manner.
    32         NSLayoutConstraint *widthFenceConstraint = [NSLayoutConstraint constraintWithItem:cell.contentView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:contentViewWidth];
    33         [cell.contentView addConstraint:widthFenceConstraint];
    34  
    35         // Auto layout engine does its math
    36         fittingHeight = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
    37         [cell.contentView removeConstraint:widthFenceConstraint];
    38  
    39         [self fd_debugLog:[NSString stringWithFormat:@"calculate using system fitting size (AutoLayout) - %@", @(fittingHeight)]];
    40     }
    41  
    42     if (fittingHeight == 0) {
    43 #if DEBUG
    44         // Warn if using AutoLayout but get zero height.
    45         if (cell.contentView.constraints.count > 0) {
    46             if (!objc_getAssociatedObject(self, _cmd)) {
    47                 NSLog(@"[FDTemplateLayoutCell] Warning once only: Cannot get a proper cell height (now 0) from '- systemFittingSize:'(AutoLayout). You should check how constraints are built in cell, making it into 'self-sizing' cell.");
    48                 objc_setAssociatedObject(self, _cmd, @YES, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    49             }
    50         }
    51 #endif
    52         // Try '- sizeThatFits:' for frame layout.
    53         // Note: fitting height should not include separator view.
    54         fittingHeight = [cell sizeThatFits:CGSizeMake(contentViewWidth, 0)].height;
    55  
    56         [self fd_debugLog:[NSString stringWithFormat:@"calculate using sizeThatFits - %@", @(fittingHeight)]];
    57     }
    58  
    59     // Still zero height after all above.
    60     if (fittingHeight == 0) {
    61         // Use default row height.
    62         fittingHeight = 44;
    63     }
    64  
    65     // Add 1px extra space for separator line if needed, simulating default UITableViewCell.
    66     if (self.separatorStyle != UITableViewCellSeparatorStyleNone) {
    67         fittingHeight += 1.0 / [UIScreen mainScreen].scale;
    68     }
    69  
    70     return fittingHeight;
    71 }

    这里作者考虑到了如果Cell使用了accessory view或者使用了系统的accessory type,需要减掉相应的宽度。接着判断如果使用了AutoLayout,则使用iOS 6提供的systemLayoutSizeFittingSize方法获取高度。如果高度为0,则尝试使用Frame Layout的方式,调用重写的sizeThatFits方法进行获取。如果还是为0,则给出默认高度并返回。

    Cell重用

    fd_templateCellForReuseIdentifier方法
     1 - (__kindof UITableViewCell *)fd_templateCellForReuseIdentifier:(NSString *)identifier {
     2     NSAssert(identifier.length > 0, @"Expect a valid identifier - %@", identifier);
     3  
     4     NSMutableDictionary *templateCellsByIdentifiers = objc_getAssociatedObject(self, _cmd);
     5     if (!templateCellsByIdentifiers) {
     6         templateCellsByIdentifiers = @{}.mutableCopy;
     7         objc_setAssociatedObject(self, _cmd, templateCellsByIdentifiers, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
     8     }
     9  
    10     UITableViewCell *templateCell = templateCellsByIdentifiers[identifier];
    11  
    12     if (!templateCell) {
    13         templateCell = [self dequeueReusableCellWithIdentifier:identifier];
    14         NSAssert(templateCell != nil, @"Cell must be registered to table view for identifier - %@", identifier);
    15         templateCell.fd_isTemplateLayoutCell = YES;
    16         templateCell.contentView.translatesAutoresizingMaskIntoConstraints = NO;
    17         templateCellsByIdentifiers[identifier] = templateCell;
    18         [self fd_debugLog:[NSString stringWithFormat:@"layout cell created - %@", identifier]];
    19     }
    20  
    21     return templateCell;
    22 }

    这里通过dequeueReusableCellWithIdentifier方法从队列中获取templateCell,并通过fd_isTemplateLayoutCell属性标识其只用来充当模板计算,并不真正进行呈现,最后通过关联对象的方式进行存取。

    注意:这里通过dequeueReusableCellWithIdentifier进行获取,也就意味着你必须对指定的Identifier先进行注册,注册可以通过以下三中方法:

    总结:

    UITableView优化方案其实还有很多,不同的场景选用不同的方案,实现效果达到预期,这才是我么最终的目标。我这里简单介绍下其他的优化的细节:

  • 相关阅读:
    MySQL服务端恶意读取客户端文件漏洞 (DDCTF2019和国赛均涉及到这个漏洞)
    (转载)基于BIGINT溢出错误的SQL注入
    程序逻辑问题
    ansible笔记
    centos6 sersync2使用
    vsftpd服务
    rsync和rsync后台模式
    mysql5.6和5.7安装 centos
    mysql5.7-my.cnf
    bind-dns服务器搭建
  • 原文地址:https://www.cnblogs.com/fengmin/p/5406769.html
Copyright © 2011-2022 走看看