App调优
1.避免UIImageView不必要的拉伸 :
如果要在UIImageView中显示一个来自bundle的图片,你应保证图片的大小和UIImageView的大小相同。在运行中缩放图片是很耗费资源的,特别是UIImageView嵌套在UIScrollView中的情况下。
如果图片是从远端服务加载的你不能控制图片大小,比如在下载前调整到合适大小的话,你可以在下载完成后,最好是用background thread,缩放一次,然后在UIImageView中使用缩放后的图片。
2.尽量使用不透明的views
3.耗时操作多用子线程
4.集合类使用的注意点
Array:有序的一组值。使用index来查找很快,使用value 查找很慢, 插入/删除很慢。
Dictionaries:存储键值对,用键来查找比较快。
Sets: 无序的一组值。用值来查找很快,插入/删除很快。
5.压缩传输数据
减少带宽
使用高效的数据交换格式
在可能的情况下使用预先压缩的数据,对音频,视频,图像进行压缩或者按比例缩放。
压缩每一个请求与响应负载。压缩文本负载以减少带宽。
最简单的方式就是在服务端和你的app中打开gzip。这对于文字这种能有更高压缩率的数据来说会有更显著的效用。
6.重用和延迟加载(lazy load) Views
更多的view意味着更多的渲染,也就是更多的CPU和内存消耗,对于那种嵌套了很多view在UIScrollView里边的app更是如此。
这里我们用到的技巧就是模仿UITableView和UICollectionView的操作: 不要一次创建所有的subview,而是当需要时才创建,当它们完成了使命,把他们放进一个可重用的队列中。
维护一个重用的队列关键点:
- 维护一个重用队列
- 当元素离开可见范围时候,removeFromSuperView并加入重用队列
- 当需要加入新元素时,先尝试从重用队列获取可重用的元素并且从重用队列移除
- 如果队列为空,新建元素。
实际使用中,需要注意的点是:
当重用对象为 view controller 时,记得 addChildeViewController
当 view 或 view controller 被重用但其对应 model 发生变化的时候,需要及时清理重用前留下的内容
数据可以适当做缓存,在重用的时候尝试从缓存中读取数据甚至之前的状态(如 table view 的 contentOffset),以得到更好的用户体验
当 on screen 的元素数量可确定的时候,有时候可以提前 init 这些元素,不会在 scroll 过程中遇到因为 init 开销带来的卡顿(尤其是以 view controller 为重用对象的时候)
7.多做Cache
一个极好的原则就是,缓存所需要的,也就是那些不大可能改变但是需要经常读取的东西。我们能缓存些什么呢?一些选项是,远端服务器的响应,图片,甚至计算结果,比如UITableView的行高。
8.正确处理内存警告
一旦系统内存过低,iOS会通知所有运行中app。在官方文档中是这样记述:
如果你的app收到了内存警告,它就需要尽可能释放更多的内存。最佳方式是移除对缓存,图片object和其他一些可以重创建的objects的strong references.
幸运的是,UIKit提供了几种收集低内存警告的方法:
在app delegate中使用applicationDidReceiveMemoryWarning: 的方法
在你的自定义UIViewController的子类(subclass)中覆盖didReceiveMemoryWarning
注册并接收 UIApplicationDidReceiveMemoryWarningNotification 的通知
一旦收到这类通知,你就需要释放任何不必要的内存使用。
例如,UIViewController的默认行为是移除一些不可见的view, 它的一些子类则可以补充这个方法,删掉一些额外的数据结构。一个有图片缓存的app可以移除不在屏幕上显示的图片。
9.重用大开销对象
一些objects的初始化很慢,比如NSDateFormatter和NSCalendar。然而,你又不可避免地需要使用它们,比如从JSON或者XML中解析数据。
想要避免使用这个对象的瓶颈你就需要重用他们,可以通过添加属性到你的class里让后使用懒加载或者创建静态变量来实现。(静态变量内存属于全app,类似于单例)
10.设定Shadow Path
如何在一个View或者一个layer上加一个shadow呢,QuartzCore框架是很多开发者的选择:
// Setup the shadow ...
UIView *view = [[UIView alloc] init];
view.layer.shadowOffset = CGSizeMake(-1.0f, 1.0f);
view.layer.shadowRadius = 5.0f; view.layer.shadowOpacity = 0.6;
用起来简单但是不是最好的处理方式,Core Animation不得不先在后台得出你的图形并加好阴影然后才渲染,这开销是很大的。
使用shadowPath的话就避免了这个问题:
view.layer.shadowPath = [[UIBezierPath bezierPathWithRect:view.bounds] CGPath];
使用shadow path的话iOS就不必每次都计算如何渲染,它使用一个预先计算好的路径。但问题是自己计算path的话可能在某些View中比较困难,且每当view的frame变化的时候你都需要去update shadow path.
11.优化tableView
– 正确使用reuseIdentifier来重用cells
尽量使所有的view opaque,包括cell自身
避免渐变,图片缩放
缓存行高
如果cell内现实的内容来自web,使用异步加载,缓存请求结果
使用shadowPath来画阴影
减少subviews的数量
尽量不适用cellForRowAtIndexPath:,如果你需要用到它,只用一次然后缓存结果
使用正确的数据结构来存储数据
使用rowHeight, sectionFooterHeight 和 sectionHeaderHeight来设定固定的高,不要请求delegate,如果是动态计算,建议同样做缓存处理。
12.选择正确的数据存储选项
缓存从业务角度可以分成两种:
按需缓存:类似浏览器,允许查看访问过的内容,可以缓存转换后的模型或者缓存url的返回值
预缓存:需要一个后台线程访问并以有意义的格式保存,以便可以在本地编辑。在有网时候把变更发到服务器。实现预缓存必须定期删除不在需要的过时数据,避免缓存不断增长。同步变更是通过追踪变更集并发送回服务器实现的。
一个App选择哪种缓存方式:是否需要在下载数据后做后期处理。后期处理包括:1.用户的编辑产生,2.更新下载数据。如重写html页面的图片链接指向本地缓存图片。
不建议用CoreData等来实现按需缓存,CoreData优势是不用反归档完整的数据就可以独立访问模型属性。而按需缓存并不需要独立访问模型属性。
当存储大块数据时你会怎么做?
你有很多选择,比如:
使用NSUerDefaults:适合小数据
使用XML, JSON, 或者 plist :你需要读取整个文件到内存里去解析,这样是很不经济的。使用SAX又是一个很麻烦的事情。
使用NSCoding存档
使用类似SQLite的本地SQL数据库
使用 Core Data
13.非必需条件下少用imageName:
然而,在图片反复重用的情况下imageNamed是一个好得多的选择。
如果你要加载一个大图片而且是一次性使用,那么就没必要缓存这个图片,用imageWithContentsOfFile足矣,这样不会浪费内存来缓存它。
那么我们应该如何选择呢?
UIImage *img = [UIImage imageNamed:@"myImage"]; // caching
UIImage *img = [UIImage imageWithContentsOfFile:@"myImage"]; // no caching
14.恰当的时候使用autorelease来避免产生内存峰值
没有加autorelease以下代码会把array里的image一口气加在到内存里面,而释放的时机是在这一次runloop结束,用autorelease包裹起来,内存释放时机为出了autorelease的时候。具体可以看黑幕背后的Autorelease
NSArray *imageNames ;
for (NSString *imageName in imageNames) {
@autoreleasepool {
UIImage *image = [UIImage imageNamed:imageName];
//process ur image here.
}
}