我想,作为一个iOS开发人员,你应该知道***DateFormatter***的实例创建操作,是多么的昂贵。 在这篇文章中,我想看一下创建***DateFormatter***实例的成本以及如何有效的缓存他们。
###实验 1.***DateFormatter***为每个日期转换创建一个新的实例。 2.***DateFormatter***对所有日期转换重复使用相同的实例。
class DateConverter { let dateFormat = "y/MM/dd @ HH:mm" func convertDatesWithUniqueFormatter(_ dates: [Date]) { for date in dates { let dateFormatter = DateFormatter() dateFormatter.dateFormat = dateFormat _ = dateFormatter.string(from: date) } } func convertDatesWithReusedFormatter(_ dates: [Date]) { let dateFormatter = DateFormatter() dateFormatter.dateFormat = dateFormat for date in dates { _ = dateFormatter.string(from: date) } } }复制代码
***convertDatesWithUniqueFormatter***代表第一个场景,***convertDatesWithReusedFormatter***第二个场景。两种方法都遵循类似的结构 - 循环遍历日期数组并将每个日期格式化为字符串表示形式,唯一的区别在于如何***DateFormatter***使用。
我们用单元测试来测试一下性能:
import XCTest@testable import Practiceclass PracticeTests: XCTestCase { var sut:DateConverter! let dates = Array(repeating: Date(), count: 100) override func setUp() { // Put setup code here. This method is called before the invocation of each test method in the class. sut = DateConverter() } override func tearDown() { // Put teardown code here. This method is called after the invocation of each test method in the class. sut = nil } func test_convertDatesWithUniqueFormatter_performance() { measure { sut.convertDatesWithUniqueFormatter(dates) } } func test_convertDatesWithReusedFormatter_performance() { measure { sut.convertDatesWithReusedFormatter(dates) } } func testExample() { // This is an example of a functional test case. // Use XCTAssert and related functions to verify your tests produce the correct results. } func testPerformanceExample() { // This is an example of a performance test case. self.measure { // Put the code you want to measure the time of here. } }}复制代码
场景一:
场景二:
从数据可以看出,如果重复创建的话,基本上每次耗时平均为0.039,但是,如果复用的话,基本上,第二次及以后,可以缩短20%的时间。可见,***DateFormatter***的创建是多么的耗时。###如何使用高性能的DateFormatter 既然已经确定重用DateFormatter实例可以提高性能,并且我们已经确定这种性能改进将带来更好的用户体验,那么问题是: “我们如何重用它?” 很简单,可以将DateFormatter实例提取到局部变量或私有属性中,因此可以重用它。
class DateFormattingHelper { static let shared = DateFormattingHelper() static let dobDateFormatter: DateFormatter = { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "y/MM/dd @ HH:mm" return dateFormatter }() func formatDOB(_ date:Date, with dateFormatter: DateFormatter) -> String { let formattedDate = dateFormatter.string(from: date) return "Date of birth:\(formattedDate)" }}复制代码
调用:
let dateFormatter = DateFormattingHelper.shared.dobDateFormatter let dobFormattedString = DateFormattingHelper.shared.formatDOB(Date(), with: dateFormatter) print(dobFormattedString)复制代码
只要我们在使用的时候,传入***DateFormatter:***就可以了,但是,这有个弊端,如果我们多个format格式怎么办呢?创建多个这个的static 属性???
###更优雅的办法
class CachedDateFormattingHelper { // MARK: - Shared static let shared = CachedDateFormattingHelper() // MARK: - Queue let cachedDateFormattersQueue = DispatchQueue(label: "com.boles.date.formatter.queue") // MARK: - Cached Formatters private var cachedDateFormatters = [String : DateFormatter]() private func cachedDateFormatter(withFormat format: String) -> DateFormatter { return cachedDateFormattersQueue.sync { let key = format if let cachedFormatter = cachedDateFormatters[key] { return cachedFormatter } let dateFormatter = DateFormatter() dateFormatter.locale = Locale(identifier: "en_US_POSIX") dateFormatter.dateFormat = format cachedDateFormatters[key] = dateFormatter return dateFormatter } } // MARK: - DOB func formatDOBDate(_ date: Date) -> String { let dateFormatter = cachedDateFormatter(withFormat: "y/MM/dd @ HH:mm") let formattedDate = dateFormatter.string(from: date) return ("Date of birth: \(formattedDate)") } // MARK: - Account func formatLastActiveDate(_ date: Date, now: Date = Date()) -> String { let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: now)! var dateFormatter = cachedDateFormatter(withFormat: "dd MMM @ HH:mm") if date > yesterday { dateFormatter = cachedDateFormatter(withFormat: "HH:mm") } let formattedDate = dateFormatter.string(from: date) return ("Last active: \(formattedDate)") } // MARK: - Post func formatPostCreatedDate(_ date: Date) -> String { let dateFormatter = cachedDateFormatter(withFormat: "d MMM 'of' y") let formattedDate = dateFormatter.string(from: date) return formattedDate } // MARK: - Commenting func formatCommentedDate(_ date: Date) -> String { let dateFormatter = cachedDateFormatter(withFormat: "dd MMM @ HH:mm") let formattedDate = dateFormatter.string(from: date) return ("Comment posted: \(formattedDate)") }}复制代码
我们用一个字典,把对应的创建过的格式的formatter存起来,每次,只要去查一下,有没有生成过这种格式的formatter,有就拿来用,没有就创建保存,并且返回出去,这样下次同样的格式就有了。这样是不是很智能,很酷???
来吧,改造你的DateFormatter吧!!!!