aa雨下思考iOS学习专题

关于iOS性能优化

2016-06-08  本文已影响1762人  ValienZh

性能优化

80% 的性能优化都是不必要的, 如果应用存在明显问题. 再逐一核对.

1. 内存的优化:

1.1 Cache选择.

需要缓存的数据原则: 那些不大可能改变但是需要经常读取的东西. 如: 服务器的响应,图片,甚至计算结果(cell的行高).

NSCache和NSDictionary类似,不同的是系统会自动回收.所以尽量使用NSCache.

1.2 选择正确的集合

2. 界面和渲染优化:

2.1 基本

  1. 在imageView设置前, 尽量先调整好图片大小. 尤其放在UIScrollView中 , 运行中自动缩放是十分耗能. 如果图片是从远端服务器加载到的, 你不能控制图片大小. 那么最好在background thread缩放到固定大小, 然后在imageView中显示.

  2. 避免使用过大的 xib,尝试为每一个Controller配置一个单独的xib, 尽可能把一个ViewController的View层次结构分散到单独的xib中去. 因为当你加载一个xib的时候所有内容都被放在内存里,包括图片,声音文件; 如果有一个不会即刻用到的View. 就违反懒加载意义了.

  3. CALayer: 采用CornerRadius 设置圆角时, 如果数量过多,例如QQ用户会话列表,那么滚动时会大幅降低应用的帧数. 所需圆角较多的情况下, 可以预先生成图片并缓存, 再使用. (更新: iOS9之后苹果已经修复,可以直接设置CornerRadius)

  4. 正确设置背景图片:

    • 如果使用小图平铺来创建背景图的话, 采用UIColor的colorWithPatternImage的渲染速度会更快.它是用来创建小的重复的的图片做背景, self.view.backgroundColor = [UIColor colorWithPatternImage:image]
    • 如果你需要加载一个大图片 且只供一次性使用, 采用imageWIthContentsOfFile 来加载 ;
    • 如果是全画幅做背景且需要多次重用, 使用imageNamed会将图片缓存 ;
    UIImageView *backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"background"]];
    

[self.view addSubview:backgroundView];
```

2.2 权衡渲染

在iOS中可以有很多方法做出漂亮的按钮. 你可以用整幅图片, 可调大小图片,或者CAlayer,CoreGraphics甚至OpenGL来画他们. 每个不同的解决方法都有不同的复杂程度和相应的性能.

简单来说: 就是用事先渲染好的图片更快. 省去绘画步骤; 但是若把所有的图片都放到bundle中, 这样就增加了体积. --- 因此, 这就是使用可变大小的图片更好的地方.

另外, 使用图片也意味着你失去了使用代码调整图片的机动性. 如果要做一个动画效果, 可能就需要多个图片了.

所以,就需要权衡空间与时间花费.

2.3 使用Sprite Sheets(游戏开发)

Sprite Sheets 可以让渲染速度加快, 甚至比标准的屏幕渲染方法节省内存 ;

3. 多线程方面

4.网络方面的优化.

  1. 避免反复处理数据 : 许多应用需要从服务器加载功能所需的JSON/XML数据, 在服务器端和客户端尽量使用相同的数据结构. 因为在内存中操作数据转换数据结构是开销很大的;(获取对应的数组结构或字典结构)

  2. JSON 相比较于 XML,优点在于解析快,更小更易于传输,但其实当你传输较大数据的时候,使用 SAX 解析 XML 时,可以做到边下载边解析,可极大的降低内存消耗 .

6.tableview的优化.

简单总结:

  1. 缓存行高
  2. cell的所有子视图都用预先创建,不需要的可以先设置隐藏
  3. 所有子视图都应该是添加到contentView上,
  4. 所有子视图都必须指定背景色,且不设置alpha
  5. cell栅格化 layer.shouldRasterize = true layer.rasterizationScale = UIScreen.mainScreen().scale
  6. 异步绘制 layer.drawsAsynchronously = true.

1. cell重用机制

UITableView只会创建一屏幕或多一点的Cell, 每当Cell滑出屏幕时, 就会放入到一个缓存池中,当要显示某一位置的cell时,会根据注册ID先去缓存池中取, 如果有,就直接拿来使用, 如果没有,才会创建;

2. 缓存动态行高

在使用UITableVIew的时候可能会遇到这种情况: UITableViewCell中的内容来自网络端,可能需要根据内容而生成高度不一致的Cell.

解决方式

2.1 常规解决方式:

先实现delegate中的cell高度的定义方法. heightForRowAtIndexPath:和数据源的cellForRowAtIndexPath, 但是cell高度定义的代理方法总是优先于cell设置的数据源方法调用,这里始终有个矛盾,即在设置cell高度的时候,cell显示数据还没有拿到;

所以,我们先要获取数据,使用+(float)cellHeighWithText:(NSString*)或其他方式计算出内容的行高,设置之后再通过数据源把内容填进去.

不足: 在这里,首先实际上对于同一条数据,内容被加载了两次;其次,在计算富文本或者大量数据大小的时候,tableview会出现卡顿.

2.2 使用NSCache

简单来说,NSCache是一个傻瓜式的缓存控件, 存取方式类似于NSDictionary, 工作方式与苹果的内存管理体系一致. 在内存紧张的时候,它会自动释放存储的对象;所以,项目中所有称之为缓存的对象都应该被被换成NSCache,代替NSArray和NSDictionary的缓存功能,(例如苹果的webView的缓存默认是交给NSCache管理的);

如果tableview数量多且不规则,我们需要做的就是在计算高度的时候就生成一个UITableViewCell或是直接cell行高并存入NSCache,需要返回Cell时, 先从缓存池中需找哪个Cell, 如果没找到则使用UITableView的重用机制重用,如果还找不到,在新建一个;

-(UITableViewCell* )tableview:(UITableView* )tableview  preparedCellForIndexPath:(NSIndexPath * )indexPath withData:(id)data {
    NSString * key = [NSString stringWithFormat:@"%ld-%ld",(long)indexPath.section,indexPath.row];
    //try to get the cell from cache.
    YQTableViewCell * cell = [_cellCache objectForKey:key];
    if(!cell) {
        //cache中没有从重用中获取
        static NSString * ID = @"cell";
        cell = (YQTableviewCell* ) [tableview dequeueReusableCellWithIdentifier:ID];
        if(!cell) { //还没有 ,创建 并存入缓存池
            cell = [[YQTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
            }
    //数据内容传给cell显示
    [cell setContentText:data]    
    //存入cache
    [_cellCache setObject:cell forked:key];
    }
    return cell;
}

7.数据存储方式

选择正确的数据存储选项 plist-归解档-偏好设置 - SQLite - Core Data.

8. 处理内存警告

系统内存过低时,iOS会通知所有的运行中app . 最佳方式是移除对缓存,图片object和其他一些可以重新创建的对象的强引用.

UIKit 提供了几种收集内存警告的方法:

收到这个通知, 你就需要释放任何不必要的内存使用, 例如: UIViewController的默认行为是移除一些不可见的view . 它的一些子类则可以补充这个方法, 删掉一些额外的数据结构. 一个有图片缓存的app可以移除不再屏幕上显示的图片 .

9. 加速启动时间

快速打开app是很重要的, 特别是第一次打开的时候 ; 所能做的就是使它在此期间尽可能的做更多的异步任务, 比如请求数据,解析数据.避免过大的xib,因为这是在主线程上加载的. 所以尽量使用sb吧. (测试启动速度要把设备从xcode断开,因为xcode debug时watchdog并不运行)

10. 手动使用Autorelease Pool

手动释放临时对象.

NSArray *urls = <# An array of file URLs #>;
for(NSURL *url in urls) {
    @autoreleasepool {
        NSError *error;
        NSString *fileContents = [NSString stringWithContentsOfURL:url
                                         encoding:NSUTF8StringEncoding error:&error];
        /* Process the string, creating and autoreleasing more objects. */
    }
}
上一篇 下一篇

猜你喜欢

热点阅读