最近在进行项目优化的时候遇到了关于null_resettable的坑,记录一下,由于之前代码不在了,简单模拟一下当时的情况.
最近项目要正式上线,需要进行一定的性能方面的测试,由于之前的数据加载的方案几经变化,都没有出现严重的性能问题,也没有在意,结果一测试,结果让我大跌眼镜:性能简直差到天边d(・`ω´・d*)!!!简单log一下看看那里耗时严重.
测试环境
硬件:iPhone5C,系统,iOS9.0.2(越狱)
网络:辣鸡WiFi…
没有模拟器,之前引入第三方蓝牙库,只有真机包,坑…,要不然直接Instruments查看了,不过当时预测不是大问题,就直接log查看了.
主要过程
思路:由于之前是采取分段加载数据,之后采取一次性加载数据,同时进行4个网络请求,可能在网络方面有耗时操作,包括请求数据,解析数据;另一个就是存在频繁调用方法的低性能,导致运行慢.
测试数据是血糖数据,数据时间跨度为两年,模拟数据3600条,主要包括空腹血糖数据,以及数据记录时间等,用于绘制曲线图,曲线图以四小时为单位进行绘制,可知共有365 * 2 * 6 = 4380
个点需要绘制,其中包括大量的时间比较,因为在同一时间区间,比如4:00-8:00只允许有一个数据进行绘制,因此这里还有一个数据去重操作.大概过程理清之后,打印一下时间:
定位问题
简单看了一下时间分布,总共有18s,网络部分,网络请求7s,其中有接近1s的数据解析耗时;绘图部分,空腹血糖数据生成耗时10s.
耗时代码:
按照以上思路,继续打印时间,最后定位问题代码
进入看一下这行代码的功能,主要用于计算两个日期之间的整数天.代码实现如下:
看了一下这几行代码,[endDate dateStringWithFormatString:@"yyyy-MM-dd"]
主要用于去掉日期的时分秒时间,[cal ordinalityOfUnit:NSDayCalendarUnit inUnit:NSEraCalendarUnit forDate:s]
是系统提供的方法,进行单独测试,没发现问题.上面的方法实现如下:
这里只是对系统方法进行简单调用,单看代码没问题,每个方法进入头文件看一下,其中一个关键字引起注意,想起了之前听说过NSDate
有性能问题,当时没注意,现在看到这个关键字,猜测是这个属性引起的性能问题:
NSDateFormatter.h
@property (null_resettable, copy) NSString *dateFormat;
使用null_resettable修饰的属性,字面意义,不可重置的,官方默认使用这个关键字,就是告诉开发者尽量不要重置这个属性的值,因为重置需要重写set和get,防止为空的情况下没有默认值,好了,就是这个坑.
解决方案
定位到问题代码,优化考虑从两方面入手,一是避免调用这个方法,二是替换这个方法的实现,换用更好性能的实现.在原先的代码中,有很多地方调用[NSDate daysWithinEraFromDate: toDate:]
,还有很多地方调用日期转字符串的方法.
首先,把简单调用日期转字符串的方法改为字符串截取方法,比如,只需要获取年月日的地方可以这样调用:
这样就可以把日期转换为yyyy-MM-dd格式,注意,需要保证date的description
返回标准格式,防止他人重写description
带来隐患,
其次,把需要进行计算日期差的方法改为下面的实现:
经过这一番修改,绘制时间缩短到了1.5s,但是网络请求时间太久,接下来就是进行数据本地缓存,网络分段加载数据等方面网络部分的优化了.