DataFormatter关于CPU消耗测试

POCT日历页面初始化的阶段在iPhone6上,会有明显的卡顿情况,通过Instrument查看,发现耗时最长的是第三方控件FSCalendar,因为FSCalendar里面的内容未开放源码处理,所以暂时无法对其进行修改。

在查看了代码后,发现日历页面有大量关于时间格式转换相关的内容,而使用DateFormatter会消耗大量的CPU,使用缓存的方式将DateFormatter对象进行缓存会在一定程度上提高效率。下面通过使用缓存和未使用缓存,通过CPU的消耗进行对比,更直观的了解缓存DateFormatter对象可以提高CPU工作效率,减少时间。未使用缓存相关的代码

// 未使用缓存

func yyyyMMddDate() -> Date?{

    let formatter = DateFormatter()

    formatter.dateFormat = "yyyy-MM-dd"

    print("yyyy-MM-dd")

    return formatter.date(from: self) }

    // 使用缓存

    func yyyyMMddDateTest() -> Date?{

        let thread = Thread.current.threadDictionary

        if thread.object(forKey: "cacheFormatterData") != nil {

            let f: DateFormatter = thread.object(forKey: "cacheFormatterData") as! DateFormatter

             return f.date(from: self)

        } else {

            let f = DateFormatter()

            f.dateFormat = "yyyy/MM/dd"

            thread.setObject(f, forKey: "cacheFormatterData" as NSCopying) return f.date(from: self)                    

}

}

通过这两个方法同样点击10次得到的CPU消耗情况如下图所示:

左边10个表示未缓存时的CPU消耗,右边是缓存了DataFormatter对象所引起的CPU消耗,很明显做了缓存的比未做缓存减少了大量的CPU消耗。所以缓存DataFormatter对象这个方案可以用到日历页面中。接下来我们可能会有新的疑问:因为什么原因使缓存DateFormatter对象CPU的消耗明显减少?

- 创建DateFormatter对象?

- 使用DateFormatter对象的属性或方法?为了验证上面提出的问题,修改了代码,重新进行测试:

 func yyyyMMddDate() -> Date? {

        let formatter = DateFormatter()

        formatter.dateFormat = "yyyy/MM/dd"

        return formatter.date(from: self)

    }

    // 使用情况 func yyyyMMddDateTest() -> Date? {

        let h = DateFormatter()

        return Date()

}

同样重复之前的验证操作,得到的结果如下图所示:

看样子创建DataFormatter对象并不是引起CPU大量消耗的原因,是不是意味着,如果我们在缓存的对象中使用了对象的属性或者方法,所产生的CPU消耗和未缓存的应该是一致的。

带着这样一个疑问,我们再对代码进行修改:

```func yyyyMMddDate() -> Date? {

        let formatter = DateFormatter()

        formatter.dateFormat = "yyyy/MM/dd"

        return formatter.date(from: self)

    }

    // 使用情况

    func yyyyMMddDateTest() -> Date? {

        let thread = Thread.current.threadDictionary

        if thread.object(forKey: "cacheFormatterData") != nil {

            let f: DateFormatter = thread.object(forKey: "cacheFormatterData") as! DateFormatter

            f.dateFormat = "yyyy/MM/dd" return f.date(from: self)

         } else {

        let f = DateFormatter()

        f.dateFormat = "yyyy/MM/dd"

        thread.setObject(f, forKey: "cacheFormatterData" as NSCopying)

        return f.date(from: self)

    }

}

重复之前的验证操作,得到的结果如下图所示:


通过这个对比,感觉非常奇怪,明明做了相同的操作,但是使用了缓存的还是明显的比未使用缓存的CPU的消耗更小,这到底是为什么?出于对这个问题存在极大困惑,仔细看了看跟threadDictionary相关的内容,争取从这些内容里面找出使什么操作减少了CPU消耗。

>苹果的官方文档对threadDictionary的描述

You can use the returned dictionary to store thread-specific data. The thread dictionary is not used during any manipulations of the NSThread object—it is simply a place where you can store any interesting data. For example, Foundation uses it to store the thread’s default NSConnection and NSAssertionHandler instances. You may define your own keys for the dictionary.

主要的意思就是threadDictionary只是可以存储任何数据的地方。

看来threadDictionary的这个猜想也不成立。

再回过来看dateFormat的定义

>@property (null_resettable, copy) NSString *dateFormat;

因为调用DateFormat对象的dateFormat属性,而这个属性会进行copy操作,而估计缓存在thread中的DateFormat对象已经进行了copy,所以不用在设置dateFormat属性时进行copy的操作。从而减少了CPU的消耗。

那么是否所有的缓存都会减少消耗呢?

我们使用POCT的NEUserDefault进行相同的缓存操作,具体的代码实现如下图所示:

 func yyyyMMddDate() -> Date? {     

      let formatter = DateFormatter()   

      formatter.dateFormat = "yyyy/MM/dd"     

      return formatter.date(from: self) 

  }     

  // 使用情况    func yyyyMMddDateTest() -> Date? {   

    if NEUserDefaults.init().object(forKey: "cacheFormatterData") != nil {     

      let f: DateFormatter = NEUserDefaults.init().object(forKey: "cacheFormatterData") as! DateFormatter       

    return f.date(from: self)   

    } else {         

          let f = DateFormatter()     

          f.dateFormat = "yyyy/MM/dd"       

          NEUserDefaults.init().setObject(f, forKey: "cacheFormatterData")     

          return f.date(from: self)   

       }    

 }

根据同样的操作我们得到以下的结果:

看到这个图的时候,是比较诧异的,做了缓存和没做缓存的,居然做了缓存的消耗的CPU更高。而这个更加明确了使用

thread.current.threadDictionary进行DataFormatter对象的缓存具有优势。

以后会探索thread.current.threadDictionary的存储内容的原理对比使用NeUserDefault使用缓存的优缺点。

tips:在上面的测试实践中,发现print方法也会明显的消耗CPU的使用。在开发过程中,发布版本应该尽量避免print的使用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,817评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,329评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,354评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,498评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,600评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,829评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,979评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,722评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,189评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,519评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,654评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,329评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,940评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,762评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,993评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,382评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,543评论 2 349

推荐阅读更多精彩内容