DateFormatter的优化

本文探讨了iOS开发中DateFormatter实例创建的高昂成本,通过实验对比重复创建与复用实例的性能差异,提出了一种利用缓存提高效率的方法,并提供了一个更优雅的解决方案,通过缓存不同格式的DateFormatter实例来应对多种格式需求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我想,作为一个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 Practice

class 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吧!!!!

转载于:https://juejin.im/post/5cb976f95188251afd79b412

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值