[性能优化]深度优化DateFormatter

2023-10-19 11:49

本文主要是介绍[性能优化]深度优化DateFormatter,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本文已收录到1.1K Star数开源学习指南——《大厂面试指北》,如果想要了解更多大厂面试相关的内容及获取《大厂面试指北》离线PDF版,请扫描下方二维码码关注公众号“大厂面试”,谢谢大家了!

《大厂面试指北》最佳阅读地址:

http://notfound9.github.io/interviewGuide/

《大厂面试指北》项目地址:

https://github.com/NotFound9/interviewGuide

获取《大厂面试指北》离线PDF版,请扫描下方二维码关注公众号“大厂面试”

《大厂面试指北》项目截图:
在这里插入图片描述

前言

在iOS开发中,对日期进行格式化处理通常有三个步骤:

  • 创建DateFormatter对象
  • 设置日期格式
  • 使用DateFormatter对象对日期进行处理

在上篇文章《DateFormatter性能优化》中,我们通过创建单例对象的方式对创建DateFormatter对象,设置日期格式两个步骤进行了缓存,将方法耗时降低为不缓存的方案的10%左右,但是这种优化方法受制于DateFormatter的几个系统方法的执行效率,本身具有一定的局限性。之前在一些文章中,也看到了使用C语言的

size_t	 strftime_l(char * __restrict, size_t, const char * __restrict,const struct tm * __restrict, locale_t)__DARWIN_ALIAS(strftime_l) __strftimelike(3);

函数对日期格式化进行处理,所以本文将对以下几种情况的方法耗时进行评测:

  • 使用Objective-C,不缓存DateFormatter对象
  • 使用Objective-C,缓存DateFormatter对象
  • 使用Objective-C,调用strftime_l做日期处理
  • 使用Swift,不缓存DateFormatter对象
  • 使用Swift,缓存DateFormatter对象
  • 使用Swift,调用strftime_l做日期处理

Objective-C的三种情况下的代码


//不缓存DateFormatter对象
-(void)testDateFormatterInOCWithoutCache:(NSInteger)times {NSString *string = @"";NSDate *date;CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();for (int i=0; i<times; i++) {NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];[dateFormatter setDateFormat:@"yyyy年MM月dd日HH时mm分ss秒"];date = [NSDate dateWithTimeIntervalSince1970:(1545308405 + i)];string = [dateFormatter stringFromDate:date];}CFAbsoluteTime duration = (CFAbsoluteTimeGetCurrent() - startTime) * 1000.0;NSLog(@"\n不缓存DateFormatter对象的方案:\n计算%ld次\n耗时%f ms\n", (long)times, duration);
}//缓存DateFormatter对象
-(void)testDateFormatterInOCWithCache:(NSInteger)times {NSString *string = @"";NSDate *date;CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();for (int i=0; i<times; i++) {date = [NSDate dateWithTimeIntervalSince1970:(1545308405 + i)];string = [[DateFormatterCache shareInstance].formatterOne stringFromDate:date];}CFAbsoluteTime duration = (CFAbsoluteTimeGetCurrent() - startTime) * 1000.0;NSLog(@"\n缓存DateFormatter对象的方案:\n计算%ld次\n耗时%f ms\n", (long)times, duration);
}//使用C语言来做日期处理
-(void)testDateFormatterInC:(NSInteger)times {NSString *string = @"";NSDate *date;time_t timeInterval;char buffer[80];CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();for (int i=0; i<times; i++) {date = [NSDate dateWithTimeIntervalSince1970:(1545308405 + i)];timeInterval = [date timeIntervalSince1970];strftime(buffer, sizeof(buffer), "%Y年%m月%d日%H时%M分%S秒", localtime(&timeInterval));string = [NSString stringWithCString:buffer encoding:NSUTF8StringEncoding];}CFAbsoluteTime duration = (CFAbsoluteTimeGetCurrent() - startTime) * 1000.0;NSLog(@"==%@", string);NSLog(@"\n使用C语言的方案:\n计算%ld次\n耗时%f ms\n", (long)times, duration);
}

这里对于咱们iOS开发的同学来说比较陌生的就是
strftime_l(buffer, sizeof(buffer), "%Y年%m月%d日%H时%M分%S秒", localtime(&timeInterval), NULL);这这行代码的调用,strftime_l函数接受四个参数,第一个参数buffer是C语言中字符数组用于存储日期格式化后的字符串,第二个参数是写入buffer数组的最大值,如果格式化的字符串大于这个值,那么只会取字符串的的一部分,第三个参数"%Y年%m月%d日%H时%M分%S秒"是日期格式,第四个参数localtime(&timeInterval)是指向使用当地时区对时间戳处理得到tm类型结构体的指针

附上tm结构体:

struct tm {int	tm_sec;		/* seconds after the minute [0-60] */int	tm_min;		/* minutes after the hour [0-59] */int	tm_hour;	/* hours since midnight [0-23] */int	tm_mday;	/* day of the month [1-31] */int	tm_mon;		/* months since January [0-11] */int	tm_year;	/* years since 1900 */int	tm_wday;	/* days since Sunday [0-6] */int	tm_yday;	/* days since January 1 [0-365] */int	tm_isdst;	/* Daylight Savings Time flag */long	tm_gmtoff;	/* offset from UTC in seconds */char	*tm_zone;	/* timezone abbreviation */
};

Swift三种情况下的代码

    //不进行缓存func testInOldWay(_ times: Int) {var string = ""var date = Date.init()let startTime = CFAbsoluteTimeGetCurrent();for i in 0..<times {let formatter = DateFormatter()formatter.dateFormat = "yyyy年MM月dd日HH时mm分ss秒"date = Date.init(timeIntervalSince1970: TimeInterval(1545308405 + i))string = formatter.string(from: date)}let duration = (CFAbsoluteTimeGetCurrent() - startTime) * 1000.0;print("使用oldWay计算\n\(times)次,总耗时\n\(duration) ms\n")}//进行缓存func testInNewWay(_ times: Int) {var string = ""var date = Date.init()let startTime = CFAbsoluteTimeGetCurrent();for i in 0..<times {date = Date.init(timeIntervalSince1970: TimeInterval(1545308405 + i))string = DateFormatterCache.shared.dateFormatterOne.string(from: date)}let duration = (CFAbsoluteTimeGetCurrent() - startTime) * 1000.0;print("使用缓存Formatter的方案计算\n\(times)次,总耗时\n\(duration) ms\n")}//使用C语言来做日期处理func testFormatterInC(_ times: Int) {var date = Date.init()var dateString = ""var buffer = [Int8](repeating: 0, count: 100)var time = time_t(date.timeIntervalSince1970)let format = "%Y年%m月%d日%H时%M分%S秒"let startTime = CFAbsoluteTimeGetCurrent();for i in 0..<times {date = Date.init(timeIntervalSince1970: TimeInterval(1545308405 + i))time = time_t(date.timeIntervalSince1970)strftime(&buffer, buffer.count, format, localtime(&time))dateString = String.init(cString: buffer, encoding: String.Encoding.utf8) ?? ""}let duration = (CFAbsoluteTimeGetCurrent() - startTime) * 1000.0;print("使用C语言的方案计算\n\(times)次,总耗时\n\(duration) ms\n")print(dateString)}

iOS 12.1 iPhone 7

测试结果:

image

测试结果:

在Objective-C中,
不使用缓存,使用缓存,使用C语言函数处理的耗时比约为100:10.7:3.5

在Swift中,
不使用缓存,使用缓存,使用C语言函数处理的耗时比约为100:11.7:6.6

Swift在使用DateFormatter进行处理时,不论是缓存的方案还是不缓存的方案,跟使用Objective-C的耗时基本一致,而在Swift中使用C语言的函数来做日期处理时,时间约为使用Objective-C的两倍,而且当只做一次日期处理时,由于涉及到一些初始资源的初始化,所以看上去比后面执行10次的时间还多

最后

如果项目是Objective-C的项目,我觉得可以采用这种C语言的strftime来做日期处理,能将时间降低为缓存NSDateFormatter的方案的33%左右,如果是Swift项目,调用C语言函数的效率没有在Objective-C项目中那么高,虽然能将时间降低为缓存NSDateFormatter的方案的56%左右,但是在Swift中使用C语言的函数存在一定的风险,在这里风险之一就是time = time_t(date.timeIntervalSince1970)这行代码返回的值是time_t类型,time_t类型的定义如下:

public typealias time_t = __darwin_time_t
public typealias __darwin_time_t = Int /* time() */

time_t其实就是Int,当Swift项目运行在32位设备(也就是iphone 5,iphone 5C)上时,Int类型是32位的,最大值为2147483647,如果这是一个时间戳的值,转换为正常时间是2038-01-19 11:14:07,也就是处理的时间是未来的日期,2038年以后的话,会出现数值溢出。

Demo在这里:
https://github.com/577528249/SwiftDemo

参考资料:

https://forums.developer.apple.com/thread/29054
https://stackoverflow.com/questions/24255020/swift-nsdate-formatting-with-strftime-localtime

PS:

最近加了一些iOS开发相关的QQ群和微信群,但是感觉都比较水,里面对于技术的讨论比较少,所以自己建了一个iOS开发进阶讨论群,欢迎对技术有热情的同学扫码加入,加入以后你可以得到:

1.技术方案的讨论,会有在大厂工作的高级开发工程师尽可能抽出时间给大家解答问题

2.每周定期会写一些文章,并且转发到群里,大家一起讨论,也鼓励加入的同学积极得写技术文章,提升自己的技术

3.如果有想进大厂的同学,里面的高级开发工程师也可以给大家内推,并且针对性得给出一些面试建议

群已经满100人了,想要加群的小伙伴们可以扫码加这个微信,备注:“加群+昵称”,拉你进群,谢谢了
image

这篇关于[性能优化]深度优化DateFormatter的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/239566

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

性能测试介绍

性能测试是一种测试方法,旨在评估系统、应用程序或组件在现实场景中的性能表现和可靠性。它通常用于衡量系统在不同负载条件下的响应时间、吞吐量、资源利用率、稳定性和可扩展性等关键指标。 为什么要进行性能测试 通过性能测试,可以确定系统是否能够满足预期的性能要求,找出性能瓶颈和潜在的问题,并进行优化和调整。 发现性能瓶颈:性能测试可以帮助发现系统的性能瓶颈,即系统在高负载或高并发情况下可能出现的问题

HDFS—存储优化(纠删码)

纠删码原理 HDFS 默认情况下,一个文件有3个副本,这样提高了数据的可靠性,但也带来了2倍的冗余开销。 Hadoop3.x 引入了纠删码,采用计算的方式,可以节省约50%左右的存储空间。 此种方式节约了空间,但是会增加 cpu 的计算。 纠删码策略是给具体一个路径设置。所有往此路径下存储的文件,都会执行此策略。 默认只开启对 RS-6-3-1024k

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

MySQL高性能优化规范

前言:      笔者最近上班途中突然想丰富下自己的数据库优化技能。于是在查阅了多篇文章后,总结出了这篇! 数据库命令规范 所有数据库对象名称必须使用小写字母并用下划线分割 所有数据库对象名称禁止使用mysql保留关键字(如果表名中包含关键字查询时,需要将其用单引号括起来) 数据库对象的命名要能做到见名识意,并且最后不要超过32个字符 临时库表必须以tmp_为前缀并以日期为后缀,备份

黑神话,XSKY 星飞全闪单卷性能突破310万

当下,云计算仍然是企业主要的基础架构,随着关键业务的逐步虚拟化和云化,对于块存储的性能要求也日益提高。企业对于低延迟、高稳定性的存储解决方案的需求日益迫切。为了满足这些日益增长的 IO 密集型应用场景,众多云服务提供商正在不断推陈出新,推出具有更低时延和更高 IOPS 性能的云硬盘产品。 8 月 22 日 2024 DTCC 大会上(第十五届中国数据库技术大会),XSKY星辰天合正式公布了基于星

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者

基于UE5和ROS2的激光雷达+深度RGBD相机小车的仿真指南(五):Blender锥桶建模

前言 本系列教程旨在使用UE5配置一个具备激光雷达+深度摄像机的仿真小车,并使用通过跨平台的方式进行ROS2和UE5仿真的通讯,达到小车自主导航的目的。本教程默认有ROS2导航及其gazebo仿真相关方面基础,Nav2相关的学习教程可以参考本人的其他博客Nav2代价地图实现和原理–Nav2源码解读之CostMap2D(上)-CSDN博客往期教程: 第一期:基于UE5和ROS2的激光雷达+深度RG

韦季李输入法_输入法和鼠标的深度融合

在数字化输入的新纪元,传统键盘输入方式正悄然进化。以往,面对实体键盘,我们常需目光游离于屏幕与键盘之间,以确认指尖下的精准位置。而屏幕键盘虽直观可见,却常因占据屏幕空间,迫使我们在操作与视野间做出妥协,频繁调整布局以兼顾输入与界面浏览。 幸而,韦季李输入法的横空出世,彻底颠覆了这一现状。它不仅对输入界面进行了革命性的重构,更巧妙地将鼠标这一传统外设融入其中,开创了一种前所未有的交互体验。 想象