[性能优化]深度优化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

相关文章

Python通过模块化开发优化代码的技巧分享

《Python通过模块化开发优化代码的技巧分享》模块化开发就是把代码拆成一个个“零件”,该封装封装,该拆分拆分,下面小编就来和大家简单聊聊python如何用模块化开发进行代码优化吧... 目录什么是模块化开发如何拆分代码改进版:拆分成模块让模块更强大:使用 __init__.py你一定会遇到的问题模www.

SpringBoot首笔交易慢问题排查与优化方案

《SpringBoot首笔交易慢问题排查与优化方案》在我们的微服务项目中,遇到这样的问题:应用启动后,第一笔交易响应耗时高达4、5秒,而后续请求均能在毫秒级完成,这不仅触发监控告警,也极大影响了用户体... 目录问题背景排查步骤1. 日志分析2. 性能工具定位优化方案:提前预热各种资源1. Flowable

SpringBoot3实现Gzip压缩优化的技术指南

《SpringBoot3实现Gzip压缩优化的技术指南》随着Web应用的用户量和数据量增加,网络带宽和页面加载速度逐渐成为瓶颈,为了减少数据传输量,提高用户体验,我们可以使用Gzip压缩HTTP响应,... 目录1、简述2、配置2.1 添加依赖2.2 配置 Gzip 压缩3、服务端应用4、前端应用4.1 N

Spring Boot + MyBatis Plus 高效开发实战从入门到进阶优化(推荐)

《SpringBoot+MyBatisPlus高效开发实战从入门到进阶优化(推荐)》本文将详细介绍SpringBoot+MyBatisPlus的完整开发流程,并深入剖析分页查询、批量操作、动... 目录Spring Boot + MyBATis Plus 高效开发实战:从入门到进阶优化1. MyBatis

SpringCloud动态配置注解@RefreshScope与@Component的深度解析

《SpringCloud动态配置注解@RefreshScope与@Component的深度解析》在现代微服务架构中,动态配置管理是一个关键需求,本文将为大家介绍SpringCloud中相关的注解@Re... 目录引言1. @RefreshScope 的作用与原理1.1 什么是 @RefreshScope1.

MyBatis 动态 SQL 优化之标签的实战与技巧(常见用法)

《MyBatis动态SQL优化之标签的实战与技巧(常见用法)》本文通过详细的示例和实际应用场景,介绍了如何有效利用这些标签来优化MyBatis配置,提升开发效率,确保SQL的高效执行和安全性,感... 目录动态SQL详解一、动态SQL的核心概念1.1 什么是动态SQL?1.2 动态SQL的优点1.3 动态S

Python如何使用__slots__实现节省内存和性能优化

《Python如何使用__slots__实现节省内存和性能优化》你有想过,一个小小的__slots__能让你的Python类内存消耗直接减半吗,没错,今天咱们要聊的就是这个让人眼前一亮的技巧,感兴趣的... 目录背景:内存吃得满满的类__slots__:你的内存管理小助手举个大概的例子:看看效果如何?1.

一文详解SpringBoot响应压缩功能的配置与优化

《一文详解SpringBoot响应压缩功能的配置与优化》SpringBoot的响应压缩功能基于智能协商机制,需同时满足很多条件,本文主要为大家详细介绍了SpringBoot响应压缩功能的配置与优化,需... 目录一、核心工作机制1.1 自动协商触发条件1.2 压缩处理流程二、配置方案详解2.1 基础YAML

Python 中的异步与同步深度解析(实践记录)

《Python中的异步与同步深度解析(实践记录)》在Python编程世界里,异步和同步的概念是理解程序执行流程和性能优化的关键,这篇文章将带你深入了解它们的差异,以及阻塞和非阻塞的特性,同时通过实际... 目录python中的异步与同步:深度解析与实践异步与同步的定义异步同步阻塞与非阻塞的概念阻塞非阻塞同步

MySQL中慢SQL优化的不同方式介绍

《MySQL中慢SQL优化的不同方式介绍》慢SQL的优化,主要从两个方面考虑,SQL语句本身的优化,以及数据库设计的优化,下面小编就来给大家介绍一下有哪些方式可以优化慢SQL吧... 目录避免不必要的列分页优化索引优化JOIN 的优化排序优化UNION 优化慢 SQL 的优化,主要从两个方面考虑,SQL 语