POCT日歷頁面初始化的階段在iPhone6上帝雇,會(huì)有明顯的卡頓情況,通過Instrument查看谱煤,發(fā)現(xiàn)耗時(shí)最長的是第三方控件FSCalendar摊求,因?yàn)镕SCalendar里面的內(nèi)容未開放源碼處理,所以暫時(shí)無法對(duì)其進(jìn)行修改。
在查看了代碼后室叉,發(fā)現(xiàn)日歷頁面有大量關(guān)于時(shí)間格式轉(zhuǎn)換相關(guān)的內(nèi)容睹栖,而使用DateFormatter會(huì)消耗大量的CPU,使用緩存的方式將DateFormatter對(duì)象進(jìn)行緩存會(huì)在一定程度上提高效率茧痕。下面通過使用緩存和未使用緩存野来,通過CPU的消耗進(jìn)行對(duì)比,更直觀的了解緩存DateFormatter對(duì)象可以提高CPU工作效率踪旷,減少時(shí)間曼氛。未使用緩存相關(guān)的代碼
// 未使用緩存
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) ? ? ? ? ? ? ? ? ? ?
}
}
通過這兩個(gè)方法同樣點(diǎn)擊10次得到的CPU消耗情況如下圖所示:
左邊10個(gè)表示未緩存時(shí)的CPU消耗,右邊是緩存了DataFormatter對(duì)象所引起的CPU消耗令野,很明顯做了緩存的比未做緩存減少了大量的CPU消耗舀患。所以緩存DataFormatter對(duì)象這個(gè)方案可以用到日歷頁面中。接下來我們可能會(huì)有新的疑問:因?yàn)槭裁丛蚴咕彺鍰ateFormatter對(duì)象CPU的消耗明顯減少气破?
- 創(chuàng)建DateFormatter對(duì)象聊浅?
- 使用DateFormatter對(duì)象的屬性或方法?為了驗(yàn)證上面提出的問題现使,修改了代碼低匙,重新進(jìn)行測試:
?func yyyyMMddDate() -> Date? {
????????let formatter = DateFormatter()
????????formatter.dateFormat = "yyyy/MM/dd"
????????return formatter.date(from: self)
????}
????// 使用情況 func yyyyMMddDateTest() -> Date? {
????????let h = DateFormatter()
????????return Date()
}
同樣重復(fù)之前的驗(yàn)證操作,得到的結(jié)果如下圖所示:
看樣子創(chuàng)建DataFormatter對(duì)象并不是引起CPU大量消耗的原因碳锈,是不是意味著顽冶,如果我們在緩存的對(duì)象中使用了對(duì)象的屬性或者方法,所產(chǎn)生的CPU消耗和未緩存的應(yīng)該是一致的售碳。
帶著這樣一個(gè)疑問强重,我們再對(duì)代碼進(jìn)行修改:
```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)
????}
}
重復(fù)之前的驗(yàn)證操作,得到的結(jié)果如下圖所示:
通過這個(gè)對(duì)比贸人,感覺非常奇怪竿屹,明明做了相同的操作,但是使用了緩存的還是明顯的比未使用緩存的CPU的消耗更小灸姊,這到底是為什么拱燃?出于對(duì)這個(gè)問題存在極大困惑,仔細(xì)看了看跟threadDictionary相關(guān)的內(nèi)容力惯,爭取從這些內(nèi)容里面找出使什么操作減少了CPU消耗碗誉。
>蘋果的官方文檔對(duì)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只是可以存儲(chǔ)任何數(shù)據(jù)的地方。
看來threadDictionary的這個(gè)猜想也不成立父晶。
再回過來看dateFormat的定義
>@property (null_resettable, copy) NSString *dateFormat;
因?yàn)檎{(diào)用DateFormat對(duì)象的dateFormat屬性哮缺,而這個(gè)屬性會(huì)進(jìn)行copy操作,而估計(jì)緩存在thread中的DateFormat對(duì)象已經(jīng)進(jìn)行了copy甲喝,所以不用在設(shè)置dateFormat屬性時(shí)進(jìn)行copy的操作尝苇。從而減少了CPU的消耗。
那么是否所有的緩存都會(huì)減少消耗呢?
我們使用POCT的NEUserDefault進(jìn)行相同的緩存操作糠溜,具體的代碼實(shí)現(xiàn)如下圖所示:
?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)? ?
? ? ? ?} ? ?
?}
根據(jù)同樣的操作我們得到以下的結(jié)果:
看到這個(gè)圖的時(shí)候淳玩,是比較詫異的,做了緩存和沒做緩存的非竿,居然做了緩存的消耗的CPU更高蜕着。而這個(gè)更加明確了使用
thread.current.threadDictionary進(jìn)行DataFormatter對(duì)象的緩存具有優(yōu)勢。
以后會(huì)探索thread.current.threadDictionary的存儲(chǔ)內(nèi)容的原理對(duì)比使用NeUserDefault使用緩存的優(yōu)缺點(diǎn)红柱。
tips:在上面的測試實(shí)踐中承匣,發(fā)現(xiàn)print方法也會(huì)明顯的消耗CPU的使用。在開發(fā)過程中锤悄,發(fā)布版本應(yīng)該盡量避免print的使用韧骗。