iOS Developer

【Objective-c】 性能优化_基本优化

2017-05-12  本文已影响97人  MR_詹
1、使用reuserIdentifer

给UITableView 和 UICollectionView ,甚至HeaderView 、FooterView 使用reuseIdentifer。因为UITableView和UICollecitonView都会将滑出屏幕的Cell放入复用池,而在每一次显示一个Cell的时候又必须重新设置和创建一个Cell,这对性能的影响是很大的,尤其是会使app的滑动体验大打折扣。
对于UITableView 和 UICollectionView的复用方法这里就不介绍了,自己问问度娘。

2、尽量将View 的opaque值设置为YES

opaque 是设置不透明度,但是改变opaque 的值不能设置View的透明度,opaque只是作为一个系统渲染视图方式的开关。opaque默认值是YES,如果view不是完全透明则设置opaque值为YES(除UIButton及其子类),那么在绘制该视图的时候把整个视图当做不透明对待,绘图系统在执行绘图过程中会优化一些操作并提供系统性能。
详细的为什么opaque的值会影响性能,请看大神解释http://blog.csdn.net/u011452278/article/details/51555806

补充:opaque 、hidden、alpha 的区别
http://blog.csdn.net/u010850094/article/details/51917793

3、不要在Image Views中调整图片大小

如果要在UIImageView中显示一个来自bundle的图片,你应保证图片的大小和UIImageView的大小相同。在运行中缩放图片是很耗费资源的,特别是UIImageView嵌套在UIScrollView中的情况下。

如果图片是从远端服务加载的你不能控制图片大小,比如在下载前调整到合适大小的话,你可以在下载完成后,最好是用background thread,缩放一次,然后在UIImageView中使用缩放后的图片。

补充:加载本地图片的正确姿势
http://blog.csdn.net/lwq718691587/article/details/51259059

/*方法1  :
  适合加载重复使用的小图片加载,可以节省每次从磁盘加载图片的时间。
  imgeNamed会将图片Cache到内存中,Cache没有明确的释放方法,
  是由系统决定什么时候释放
*/
UIImage *imag1 = [UIImage imageNamed:@"image.png"];  

/*
  方法2和方法3,这两种方法只是简单的加载图片,并不会将图片缓存起来,
  图像会被系统以数据方式加载到程序。当不需要重复使用这个图片或者图片的体积
  较大时可以使用这种方式。
*/
//方法2  :
UIImage *image2 = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"image@2x" ofType:.png]];  
//方法3  
NSData *imageData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"image@2x" ofType:png]];  
UIImage *image3 = [UIImage imageWithData:imageData]
4、不要堵塞主线程

永远不要使主线程承担过多。因为UIKit在主线程上做所有工作,渲染,管理触摸反应,回应输入等都需要在它上面完成。

一直使用主线程的风险就是如果你的代码真的block了主线程,你的app会失去反应。
大部分阻碍主进程的情形是你的app在做一些牵涉到读写外部资源的I/O操作,比如存储或者网络。

可以使用NSURLConnection异步地做网络操作:

+ (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler

或者使用像 AFNetworking这样的框架来异步地做这些操作。
如果你需要做其它类型的需要耗费巨大资源的操作(比如时间敏感的计算或者存储读写)那就用 Grand Central Dispatch,或者 NSOperation 和 NSOperationQueues.

5、延时加载(lazy load) Views

更多的view意味着更多的渲染,也就是更多的CPU和内存消耗,对于那种嵌套了很多view在UIScrollView里边的app更是如此。
这里我们用到的技巧就是模仿UITableViewUICollectionView的操作: 不要一次创建所有的subview,而是当需要时才创建,当它们完成了使命,把他们放进一个可重用的队列中。
这样的话你就只需要在滚动发生时创建你的views,避免了不划算的内存分配。
创建views的能效问题也适用于你app的其它方面。想象一下一个用户点击一个按钮的时候需要呈现一个view的场景。有两种实现方法:

  1. 创建并隐藏这个view当这个screen加载的时候,当需要时显示它;
  2. 当需要时才创建并展示。
    每个方案都有其优缺点。
    用第一种方案的话因为你需要一开始就创建一个view并保持它直到不再使用,这就会更加消耗内存。然而这也会使你的app操作更敏感因为当用户点击按钮的时候它只需要改变一下这个view的可见性。

第二种方案则相反-消耗更少内存,但是会在点击按钮的时候比第一种稍显卡顿。

提醒
使用懒加载时有一点是要注意的:当调用getter方法,懒加载开始初始化,并返回实例对象,如果懒加载代码还没有初始化完成并返回结果,在第二次调用getter方法,会再懒加载一次,实例多一个对象。(就是线程安全问题)

6、Cache 缓存

一个极好的原则就是,缓存所需要的,也就是那些不大可能改变但是需要经常读取的东西。

我们能缓存些什么呢?一些选项是,远端服务器的响应,图片,甚至计算结果,比如UITableView的行高。

对于缓存还有一个重要的内容就是"内存的优化",这是很多面试中会提到的,小编会在另外一篇文章做个小结

7、处理内存警告

一旦系统内存过低,iOS会通知所有运行中app。在官方文档中是这样记述:

如果你的app收到了内存警告,它就需要尽可能释放更多的内存。最佳方式是移除对缓存,图片object和其他一些可以重创建的objects的strong references.

幸运的是,UIKit提供了几种收集低内存警告的方法:

在app delegate中使用applicationDidReceiveMemoryWarning: 的方法
在你的自定义UIViewController的子类(subclass)中覆盖didReceiveMemoryWarning
注册并接收 UIApplicationDidReceiveMemoryWarningNotification 的通知
一旦收到这类通知,你就需要释放任何不必要的内存使用。

例如,UIViewController的默认行为是移除一些不可见的view, 它的一些子类则可以补充这个方法,删掉一些额外的数据结构。一个有图片缓存的app可以移除不在屏幕上显示的图片。

这样对内存警报的处理是很必要的,若不重视,你的app就可能被系统杀掉。

然而,当你一定要确认你所选择的object是可以被重现创建的来释放内存。一定要在开发中用模拟器中的内存提醒模拟去测试一下。

内存警告方法调用次序:
http://www.cnblogs.com/starainDou/p/5124727.html
内容警告处理方法
http://www.jianshu.com/p/7969fe37ea33

8、重用大开销对象

一些objects的初始化很慢,比如NSDateFormatter和NSCalendar。然而,你又不可避免地需要使用它们,比如从JSON或者XML中解析数据。

想要避免使用这个对象的瓶颈你就需要重用他们,可以通过添加属性到你的class里或者创建静态变量来实现。

注意如果你要选择第二种方法,对象会在你的app运行时一直存在于内存中,和单例(singleton)很相似。

下面的代码说明了使用一个属性来延迟加载一个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;
}

还需要注意的是,其实设置一个NSDateFormatter的速度差不多是和创建新的一样慢的!所以如果你的app需要经常进行日期格式处理的话,你会从这个方法中得到不小的性能提升。

上一篇下一篇

猜你喜欢

热点阅读