UITableView性能优化

UITableView性能优化

总结一些UITableView性能优化方案:

  • 预排版

    根据JSON数据后台线程进行渲染排版得到包含文本宽高、子视图高度和cell高度的CellLayout对象。CellLayout对象占用内存小,可缓存,TableView 在请求各个cell高度时,不会消耗任何多余计算量。

  • 预渲染

    图片圆角使用maskToBounds和cornerRadius属性会造成GPU离屏渲染,性能较差。使用CoreGraphic后台渲染圆角图片, 主要原理是使用CGContext绘制图片,然后添加圆角Path, 使用clip进行剪切。值得注意的是,渲染后的图片也要缓存到图片缓存中,以避免多次渲染。

    // UIView渲染
    
    func xy_drawRectWithRoundedCorner(size: CGSize, radius: CGFloat, borderWidth: CGFloat, backgroundColor: UIColor, borderColor: UIColor) -> UIImage? {
    
        var image: UIImage? = nil
    
        let width = size.width
        let height = size.height
        let offSetY = height/2
        let offSetX = width/2
    
        UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale)
    
        guard let context = UIGraphicsGetCurrentContext() else {
            return image
        }
    
        context.setStrokeColor(borderColor.cgColor)
        context.setLineWidth(borderWidth)
        context.setFillColor(backgroundColor.cgColor)
        context.addArc(center: CGPoint(x: offSetX, y: offSetY), radius: radius-borderWidth, startAngle: 0, endAngle: CGFloat(2*Double.pi), clockwise: true)
        context.drawPath(using: .fillStroke)
    
        image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
    
        return image
    }
    
    // UIImage后台渲染圆角
    
    extension UIImage {
        func xy_drawRectWithRoundedCorner(radius: CGFloat, sizetoFit: CGSize, finished: @escaping (UIImage?) -> Void) {
            DispatchQueue.global().async {
                var image: UIImage? = nil
                UIGraphicsBeginImageContextWithOptions(sizetoFit, false, UIScreen.main.scale)
                guard let context = UIGraphicsGetCurrentContext() else {
                    DispatchQueue.main.async {
                        finished(nil)
                    }
                    return
                }
                let rect = CGRect(origin: .zero, size: sizetoFit)
    
                context.addPath(UIBezierPath(roundedRect: rect, byRoundingCorners: UIRectCorner.allCorners,
                                             cornerRadii: CGSize(width: radius, height: radius)).cgPath)
                context.clip()
    
                self.draw(in: rect)
                context.drawPath(using: .fillStroke)
    
                image = UIGraphicsGetImageFromCurrentImageContext()
                UIGraphicsEndImageContext()
    
                DispatchQueue.main.async {
                    finished(image)
                }
            }
        }
    }
    
  • 异步绘制

    详情参考 YYAsyncLayer原理。YYAsyncLayer异步绘制的过程大致如下:

    - (void)display {
        dispatch_async(backgroundQueue, ^{
            CGContextRef ctx = CGBitmapContextCreate(...);
            // draw in context...
            CGImageRef img = CGBitmapContextCreateImage(ctx);
            CFRelease(ctx);
            dispatch_async(mainQueue, ^{
                layer.contents = img;
            });
        });
    }
    
  • 减少Cell视图层级

    视图层级较多时,GPU图层混合阶段会更耗时,会造成卡顿。

  • 视图不透明translucent = true

    视图设置半透明时,GPU图层混合、纹理渲染阶段会更耗时,会造成卡顿。

  • 图片异步解码

    CPU工作流程的准备阶段,默认主线程解码图片,比较消耗资源。

  • 禁用离屏渲染

    cornerRadius和maskToBounds同时使用可导致离屏渲染(采用预渲染可消除)

    设置shadow相关属性可导致离屏渲染(设置shadowPath可消除)

    设置layer的mask属性可导致离屏渲染

    设置可光栅化shouldRasterize = true也可导致离屏渲染