关于iOS性能优化

性能优化

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

1. 内存的优化:

  • 重用和懒加载Views.
    • 模仿tableview进行重用: 不要一次创建所有的SubViews . 而当需要时才创建, 当使用过后, 放入可重用的缓存池.
    • 一般在scrollView中滚动时创建你的views,避免不必要的内存分配.
    • 也可以在用户点击了一个按钮需要 创建呈现一个view的场景.
  • 重用大开销对象 . 一些object的初始化很慢,比如NSDateFormatterNSCalendar, 然后有时不可避免的需要使用它们. 可以通过属性记录或者静态变量(这种方式会同单例一样在app运行时一直存在.) 创建;

1.1 Cache选择.

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

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

1.2 选择正确的集合

  • Arrays: 使用index来lookup很快, 但是使用value lookup很慢. 同样插入/删除都很慢.
  • Dictionaries: 用key查找比较快;
  • Sets: 无序的. 用值查找很快, 插入和删除很快 ;

2. 界面和渲染优化:

2.1 基本

  • 尽量将不透明view的Opaque属性设置为true. 尤其是他们出现在赋值动画或是ScrollView中. 通常我们可以对tableview中的cell做此设置.(这样可以让系统用一个最优的方式渲染这些views. 你可以在模拟器中用Debug\Color Blended Layers选项来发现哪些view没有被设置为opaque。目标就是,能设为opaque的就全设为opaque!).
  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];
```

  • 设置Shadow Path: 如何在一个View或者layer上加一个shadow, QuartzCore框架是很多人第一选择. 但是Core Animation不得不先在后台得出你的图形并并加好阴影之后才渲染. 这开销很大. 可以使用shadowPath避免这个问题 . view.layer.shadowPath = [[UIBezierPath bezierPathWithRect:view.bounds] CGPath]; 这样iOS就不必每次都计算如何渲染, 它使用一个预先计算好的路径.不过可能在某些View计算path比较困难,且当View的frame变化时,你都需要区update shadowPath.
  • 尽量不要在viewWillAppear中进行太多操作.

2.2 权衡渲染

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

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

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

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

2.3 使用Sprite Sheets(游戏开发)

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

3. 多线程方面

  • 除渲染,触摸响应,UI操作 等,尽量都使用异步来处理: 如 I/O,存储,网络,异步线程通知.

4.网络方面的优化.

  • 网络响应NSURLConnection缓存策略.
  • 当传输网络数据过大时, 采用gzip压缩方式可以一定程度降低文件大小.(AFN框架已支持) gzip;
  1. 避免反复处理数据 : 许多应用需要从服务器加载功能所需的JSON/XML数据, 在服务器端和客户端尽量使用相同的数据结构. 因为在内存中操作数据转换数据结构是开销很大的;(获取对应的数组结构或字典结构)

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

  • 减少使用Web特性: 想要更高的性能你就要调整下你的HTML了。
    • 第一件要做的事就是尽可能移除不必要的javascript,避免使用过大的框架。能只用原生js就更好了。
    • 另外,尽可能异步加载例如用户行为统计script这种不影响页面表达的javascript。
    • 最后,永远要注意你使用的图片,保证图片的符合你使用的大小。使用Sprite sheet提高加载速度和节约内存。

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先去缓存池中取, 如果有,就直接拿来使用, 如果没有,才会创建;

  • 当cell里需加载网络图片的话, 采用异步方式(如直接使用SDWebImage),且先放置一个默认图片, 去的网络数据后再更新.
  • 如果单个cell所需网络数据的请求时间过长, 可以选择在tableview停止滑动之后再加载网络数据, 可以利用UIScrollViewDelegate中的scrollViewDidEndDragging和scrollViewDidEndDecelerating来获知停止滑动. 还可以在scrollViewWillBeginDragging函数做,添加代码来停止正在进行的数据加载;
  • 尽量使用rowHeigh, sectionFooterHeight,sectionHeaderHeight 来设置固定的高,而不是请求代理,即便需要请求,也不要在代理方法内计算行高, 提前缓存好 . 如标题2.
  • 避免渐变,图片缩放,后台选人
  • 尽量使所有的view opaque,包括cell自身
  • 减少Subviews的数量
  • 使用shadowPath来画阴影
  • 尽量不适用或减少放置在CellForRowAtIdexPath方法内的操作.如果你需要用到它,只用一次然后缓存结果
  • 使用正确的数据结构来存储数据

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.

  • 小规模数据的使用NSUserDefaults 存储是比较好的方式 .
  • plist(XML),是需要读取整个文件到内存中解析, 这个不是很实惠.
  • 归解档: 同样有上面问题.
  • 对于较大数据使用SQLite 或Core Data,SQLite进行大量数据操作时需要关闭事务,可节省大量时间.

8. 处理内存警告

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

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

  • app delegate 中的 applicationDidReceiverMemoryWarning 的方法
  • 在自定义控制器的子类中覆盖didReceiverMemoryWarning方法
  • 自己注册并接受 UIApplicationDidReceiverMemoryWarningNotification 通知.

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

9. 加速启动时间

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

  • 在didFInishLaunching 里 开始尽可能多的异步任务来使你的应用尽早得到展示数据

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. */
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容