美文网首页
DateFormatter的优化

DateFormatter的优化

作者: AndreaArlex | 来源:发表于2018-12-21 17:40 被阅读104次

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

    场景一:


    image.png

    场景二:

    image.png
    从数据可以看出,如果重复创建的话,基本上每次耗时平均为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吧!!!!

    相关文章

      网友评论

          本文标题:DateFormatter的优化

          本文链接:https://www.haomeiwen.com/subject/wkrvkqtx.html