解决卡顿

卡顿产生原因:屏幕上每一帧内容,都是CPU计算后,GPU渲染的结果。当垂直信号(Vsync)产生时,如果CPU计算与GPU渲染的结果没有完成,屏幕会渲染上一帧的内容,从而发生丢帧。(按照60FPS的刷帧率,每隔16ms就会有一次VSync信号)。
主要思路:尽可能减少CPU和GPU的资源消耗。

具体方案:

  1. 在单元格高度不一的界面中,首先要根据数据结构,将单元格高度计算好,缓存在高度数组中,利用代理将对应高度返回。注意:不要频繁调用UIView的frame、transform、bounds,减少CPU的计算
  2. 可以用CALayer代替某些下划线之类的视图,或者用不到事件处理的地方,即尽量用轻量级对象。UIView之所以可以显示,是因为它有CALayer。除此之外UIView有处理事件的能力
  3. Auto layout会比直接设置frame消耗更多的CPU资源
  4. 圆角设置方式:
  1. 圆角使用CoreGraphics渲染
  2. 美工直接给圆角图片
  3. 比直接maskToBounds=YES; cornerRadius>0(二者同时设置)
    因为第3种是离屏渲染,需要再当前屏幕缓冲区以外开辟一个新的缓冲区进行渲染操作。离屏渲染的其他操作还有:layer.mask(遮罩)、layer.sgiykdRasterize=YES
// 0.加载图片
    UIImage *image = [UIImage imageNamed:@"头像"];
    // 1.开启位图上下文,跟图片尺寸一样大
    UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
    // 2.设置圆形裁剪区域,正切与图片
    // 2.1创建圆形的路径
    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
    // 2.2把路径设置为裁剪区域
    [path addClip];
    // 3.绘制图片
    [image drawAtPoint:CGPointZero];
    // 4.从上下文中获取图片
    UIImage *clipImage = UIGraphicsGetImageFromCurrentImageContext();
    // 5.关闭上下文
    UIGraphicsEndImageContext();
    _imageView.image = clipImage;
// Graphics绘制方法
- (void)drawCornerPicture{
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(200, 400, 200, 200)];
    imageView.image = [UIImage imageNamed:@"1"];
    // 开启图片上下文
    // UIGraphicsBeginImageContext(imageView.bounds.size);
    // 一般使用下面的方法
    UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, 0);
    // 绘制贝塞尔曲线
    UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:imageView.bounds cornerRadius:100];
    // 按绘制的贝塞尔曲线剪切
    [bezierPath addClip];
    // 画图
    [imageView drawRect:imageView.bounds];
    // 获取上下文中的图片
    imageView.image = UIGraphicsGetImageFromCurrentImageContext();
    // 关闭图片上下文
    UIGraphicsEndImageContext();
    [self.view addSubview:imageView];
}
  1. 图片的size最好和UIImageView的size保持一致,减少CPU的压缩计算
  2. 控制线程的最大并发量
  3. 尽量把耗时操作放入子线程,例如:
  1. boundingReactWithSize(计算文本)
  2. drawWithReact(文本绘制)
  3. 图片异步解码:[UIImage imageNamed:]返回的对象,只有当图片渲染时,才会被解码,而且这个解码方式在主线程。因此在异步处理好解码的结果直接渲染,可以减少耗时(图片解码)
+ (nullable UIImage *)decodedImageWithImage:(nullable UIImage *)image {
    if (![UIImage shouldDecodeImage:image]) {
        return image;
    }

    @autoreleasepool{
        
        CGImageRef imageRef = image.CGImage;
        CGColorSpaceRef colorspaceRef = [UIImage colorSpaceForImageRef:imageRef];
        
        size_t width = CGImageGetWidth(imageRef);
        size_t height = CGImageGetHeight(imageRef);
        size_t bytesPerRow = kBytesPerPixel * width;
        // 图片绘制
        CGContextRef context = CGBitmapContextCreate(NULL,
                                                     width,
                                                     height,
                                                     kBitsPerComponent,
                                                     bytesPerRow,
                                                     colorspaceRef,
                                                     kCGBitmapByteOrderDefault|kCGImageAlphaNoneSkipLast);
        if (context == NULL) {
            return image;
        }
        // 绘制图片
        CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
        CGImageRef imageRefWithoutAlpha = CGBitmapContextCreateImage(context);
         // 获取图片 ( 完成解码) 
        UIImage *imageWithoutAlpha = [UIImage imageWithCGImage:imageRefWithoutAlpha
                                                         scale:image.scale
                                                   orientation:image.imageOrientation];
        
        CGContextRelease(context);
        CGImageRelease(imageRefWithoutAlpha);
        
        return imageWithoutAlpha;
    }
}

+ (BOOL)shouldDecodeImage:(nullable UIImage *)image {
    // Prevent "CGBitmapContextCreateImage: invalid context 0x0" error
    if (image == nil) {
        return NO;
    }
    if (image.images != nil) {
        return NO;
    }
    
    CGImageRef imageRef = image.CGImage;
    
    CGImageAlphaInfo alpha = CGImageGetAlphaInfo(imageRef);
    BOOL anyAlpha = (alpha == kCGImageAlphaFirst ||
                     alpha == kCGImageAlphaLast ||
                     alpha == kCGImageAlphaPremultipliedFirst ||
                     alpha == kCGImageAlphaPremultipliedLast);
    if (anyAlpha) {
        return NO;
    }
    
    return YES;
}

耗电优化

  1. 尽可能降低CPU、GPU功耗
  2. 少用定时器
  3. 优化I/O操作(文件读写)
  1. 尽量不要频繁写入小数据,最好批量一次性写入
  2. 访问大量数据,dispatch_io提供了基于GCD的异步操作文件I/O的API,可以优化磁盘访问
    3.数据较大用数据库

网络优化

  1. 减少、压缩网络数据。(XML数据格式臃肿,所以JSON流行)
  2. 最好一次性下载完,减少请求次数。反之,使用断点续传,保障数据不重复请求
  3. 网络不可用时,停止执行所有网络请求

定位优化

  1. 如果定位完成,最好关闭定位服务,这样可以让定位硬件断电
  2. 尽量不使用定位精度最高的KCLLocationAccuracyBest
  3. 需要后台定位时,尽量设置pauseLocationUpdatesAutomatically = YES;在用户很小移动时,会暂停位置更新