iOS性能优化的25条建议
入门级
1.用ARC管理内存
2.在正确的地方使用reuseIdentifier(cell默认标识符)
3. 尽可能使Views不透明(提升渲染速度)
设置Views的opaque属性为YES(默认)。
4. 避免庞大的XIB
使XIB尽量简单。尝试为每个Controller配置一个单独的XIB,尽可能把一个View Controller的view层次结构分散到单独的XIB中去。
5.不要block主线程
永远不要使主线程承担过多。因为UIKit在主线程上做所有工作,渲染,管理触摸反应,回应输入等都需要在它上面完成。大部分阻碍主进程的情形是你的app在做一些牵涉到读写外部资源的I/O操作,比如存储或者网络
6.在Image Views中调整图片大小
如果要在`UIImageView`中显示一个来自bundle的图片,你应保证图片的大小和UIImageView的大小相同。如果图片是远端服务加载的,你可以在下载完成后,最好是用background thread,缩放一次,然后在UIImageView中使用缩放后的图片。
[self performSelectorInBackground:@selector(disposedealImage:) withObject:docids];
7.选择正确的Collection
选择对业务场景最合适的类或者对象,如:
Arrays: 有序的一组值。 使用index来查找很快,使用value查找很慢, 插入/删除很慢。
Dictionaries: 存储键值对。 用键来查找比较快。
Sets: 无序的一组值。 用值来查找很快,插入/删除很快
8.打开gzip压缩(减小文档大小)
中级
9.重用和延迟加载Views
模仿`UITableView`和`UICollectionView`的操作: 不要一次创建所有的subview,而是当需要时才创建,当它们完成了使命,把他们放进一个可重用的队列中。这样的话你就只需要在滚动发生时创建你的views,避免了不划算的内存分配。
10.Cache, Cache, 还是Cache!
缓存所需要的,也就是那些不大可能改变但是需要经常读取的东西。远端服务器的响应,图片,甚至计算结果,比如UITableView的行高。
11.权衡渲染方法
用事先渲染好的图片更快一些,因为如此一来,iOS就免去了创建一个图片再画东西上去,然后显示在屏幕上的程序。问题是你需要把所有你需要用到的图片放到app的bundle里面,这样就增加了体积。 这就是使用可变大小的图片更好的地方了: 你可以省去一些不必要的空间,也不需要再为不同的元素(比如按钮)来做不同的图。总得来说,你需要权衡一下利弊,到底是要性能能还是要bundle保持合适的大小。
12.处理内存警告
如果你的app收到了内存警告,它就需要尽可能释放更多的内存。最佳方式是移除对缓存、图片、object和其他一些可以重创建的objects的strong references。幸运的是,UIKit提供了几种收集低内存警告的方法:
在app delegate中使用`applicationDidReceiveMemoryWarning:` 的方法
在你的自定义UIViewController的子类(subclass)中覆盖`didReceiveMemoryWarning`
注册并接收 UIApplicationDidReceiveMemoryWarningNotification 的通知
一旦收到这类通知,你就需要释放任何不必要的内存使用。
例如,UIViewController的默认行为是移除一些不可见的view, 它的一些子类则可以补充这个方法,删掉一些额外的数据结构。一个有图片缓存的app可以移除不在屏幕上显示的图片。
13.重用大开销的对象
下面的代码说明了使用一个属性来延迟加载(懒加载)一个date formatter. 第一次调用时它会创建一个新的实例,以后的调用则将返回已经创建的实例:
// in your .h or inside a class extension
@property (nonatomic, strong) NSDateFormatter *formatter;
// inside the implementation (.m)
// When you need, just use self.formatter
- (NSDateFormatter *)formatter {
if (! _formatter) {
_formatter = [[NSDateFormatter alloc] init];
_formatter.dateFormat = @"EEE MMM dd HH:mm:ss Z yyyy"; // twitter date format
}
return _formatter;
}
14. 使用Sprite Sheets(游戏开发)
15. 避免反复处理数据
在服务器端和客户端使用相同的数据结构。
16.选择正确的数据格式
解析JSON会比XML更快一些,JSON也通常更小更便于传输。但是XML也有XML的好处,比如使用SAX来解析XML就像解析本地文件一样,你不需像解析json一样等到整个文档下载完成才开始解析。当你处理很大的数据的时候就会极大地减低内存消耗和增加性能。
17.正确地设定Background Images
如果你使用全画幅的背景图,就使用UIImageView,因为UIColor的colorWithPatternImage是用小图平铺来创建背景的。
18.减少使用Web特性
尽可能移除不必要的javascript,避免使用过大的框架。能只用原生js就更好了。另外,尽可能异步加载javascript。保证图片符合你使用的大小,使用Sprite sheet提高加载速度和节约内存。
19.设定Shadow Path(关于视图)
20. 优化你的Table View
正确使用`reuseIdentifier`来重用cells
尽量使所有的view opaque,包括cell自身
避免渐变,图片缩放,后台选人
缓存行高
如果cell内实现的内容来自web,使用异步加载,缓存请求结果
使用`shadowPath`来画阴影
减少subviews的数量
尽量不使用`cellForRowAtIndexPath:`,如果你需要用到它,只用一次然后缓存结果
使用正确的数据结构来存储数据
使用`rowHeight`, `sectionFooterHeight` 和 `sectionHeaderHeight`来设定固定的高,不要请求delegate
21.选择正确的数据存储选项
使用`NSUerDefaults`
使用XML, JSON, 或者 plist
使用NSCoding存档
使用类似SQLite的本地SQL数据库
使用 Core Data
NSUserDefaults:虽然它很nice也很便捷,但是它只适用于小数据,比如一些简单的布尔型的设置选项,再大点你就要考虑其它方式了
XML:总体来说,你需要读取整个文件到内存里去解析,这样是很不经济的。使用SAX又是一个很麻烦的事情。
NSCoding:不幸的是,它也需要读写文件,所以也有以上问题。
在这种应用场景下,使用SQLite 或者 Core Data比较好。使用这些技术你用特定的查询语句就能只加载你需要的对象。
在性能层面来讲,SQLite和Core Data是很相似的。他们的不同在于具体使用方法。Core Data代表一个对象的graph model,但SQLite就是一个DBMS。Apple在一般情况下建议使用Core Data,但是如果你有理由不使用它,那么就去使用更加底层的SQLite吧
进阶级
22.加速启动时间
使它尽可能做更多的异步任务,比如加载远端或者数据库数据,解析数据。避免过于庞大的XIB,因为他们是在主线程上加载的。尽量使用Storyboards!
注意,用Xcode debug时watchdog并不运行,一定要把设备从Xcode断开来测试启动速度
23.使用Autorelease Pool
NSAutoreleasePool`负责释放block中的autoreleased objects。一般情况下它会自动被UIKit调用。但是有些状况下你也需要手动去创建它。
24.选择是否缓存图片
imageNamed的优点是当加载时会缓存图片。相反的,imageWithContentsOfFile仅加载图片。如果你要加载一个大图片而且是一次性使用,那么就没必要缓存这个图片,用`imageWithContentsOfFile`足矣,这样不会浪费内存来缓存它。 然而,在图片反复重用的情况下`imageNamed`是一个好得多的选择。
25.尽量避免日期格式转换
如果你可以控制你所处理的日期格式,尽量选择Unix时间戳。你可以方便地从时间戳转换到NSDate:
- (NSDate*)dateFromUnixTimestamp:(NSTimeInterval)timestamp {
return [NSDate dateWithTimeIntervalSince1970:timestamp];
}
需要注意的是,许多web API会以微秒的形式返回时间戳,因为这种格式在javascript中更方便使用。记住用`dateFromUnixTimestamp`之前除以1000就好了。