[性能优化]深度优化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中递归下降解析器的原理与实现》在编译器设计、配置文件处理和数据转换领域,递归下降解析器是最常用且最直观的解析技术,本文将详细介绍递归下降解析器的原理与实现,感兴趣的小伙伴可以跟随... 目录引言:解析器的核心价值一、递归下降解析器基础1.1 核心概念解析1.2 基本架构二、简单算术表达

深度解析Java @Serial 注解及常见错误案例

《深度解析Java@Serial注解及常见错误案例》Java14引入@Serial注解,用于编译时校验序列化成员,替代传统方式解决运行时错误,适用于Serializable类的方法/字段,需注意签... 目录Java @Serial 注解深度解析1. 注解本质2. 核心作用(1) 主要用途(2) 适用位置3

Java MCP 的鉴权深度解析

《JavaMCP的鉴权深度解析》文章介绍JavaMCP鉴权的实现方式,指出客户端可通过queryString、header或env传递鉴权信息,服务器端支持工具单独鉴权、过滤器集中鉴权及启动时鉴权... 目录一、MCP Client 侧(负责传递,比较简单)(1)常见的 mcpServers json 配置

从原理到实战解析Java Stream 的并行流性能优化

《从原理到实战解析JavaStream的并行流性能优化》本文给大家介绍JavaStream的并行流性能优化:从原理到实战的全攻略,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的... 目录一、并行流的核心原理与适用场景二、性能优化的核心策略1. 合理设置并行度:打破默认阈值2. 避免装箱

Maven中生命周期深度解析与实战指南

《Maven中生命周期深度解析与实战指南》这篇文章主要为大家详细介绍了Maven生命周期实战指南,包含核心概念、阶段详解、SpringBoot特化场景及企业级实践建议,希望对大家有一定的帮助... 目录一、Maven 生命周期哲学二、default生命周期核心阶段详解(高频使用)三、clean生命周期核心阶

Python实战之SEO优化自动化工具开发指南

《Python实战之SEO优化自动化工具开发指南》在数字化营销时代,搜索引擎优化(SEO)已成为网站获取流量的重要手段,本文将带您使用Python开发一套完整的SEO自动化工具,需要的可以了解下... 目录前言项目概述技术栈选择核心模块实现1. 关键词研究模块2. 网站技术seo检测模块3. 内容优化分析模

Java实现复杂查询优化的7个技巧小结

《Java实现复杂查询优化的7个技巧小结》在Java项目中,复杂查询是开发者面临的“硬骨头”,本文将通过7个实战技巧,结合代码示例和性能对比,手把手教你如何让复杂查询变得优雅,大家可以根据需求进行选择... 目录一、复杂查询的痛点:为何你的代码“又臭又长”1.1冗余变量与中间状态1.2重复查询与性能陷阱1.

Python内存优化的实战技巧分享

《Python内存优化的实战技巧分享》Python作为一门解释型语言,虽然在开发效率上有着显著优势,但在执行效率方面往往被诟病,然而,通过合理的内存优化策略,我们可以让Python程序的运行速度提升3... 目录前言python内存管理机制引用计数机制垃圾回收机制内存泄漏的常见原因1. 循环引用2. 全局变

深度剖析SpringBoot日志性能提升的原因与解决

《深度剖析SpringBoot日志性能提升的原因与解决》日志记录本该是辅助工具,却为何成了性能瓶颈,SpringBoot如何用代码彻底破解日志导致的高延迟问题,感兴趣的小伙伴可以跟随小编一起学习一下... 目录前言第一章:日志性能陷阱的底层原理1.1 日志级别的“双刃剑”效应1.2 同步日志的“吞吐量杀手”

深度解析Python yfinance的核心功能和高级用法

《深度解析Pythonyfinance的核心功能和高级用法》yfinance是一个功能强大且易于使用的Python库,用于从YahooFinance获取金融数据,本教程将深入探讨yfinance的核... 目录yfinance 深度解析教程 (python)1. 简介与安装1.1 什么是 yfinance?