在快手做分享
前滴滴同事邀請(qǐng)我去快手做分享。下面是分享時(shí)的 Slides:
詳細(xì)文章介紹:如何對(duì) iOS 啟動(dòng)階段耗時(shí)進(jìn)行分析 | 星光社 - 戴銘的博客
代碼:GitHub - ming1016/MethodTraceAnalyze: 方法耗時(shí)分析
無(wú)用類檢查
如果包里有一堆沒(méi)用的類嘹黔,不光會(huì)影響用戶下載速度账嚎,也會(huì)影響啟動(dòng)加載速度。檢查無(wú)用類儡蔓,一次是無(wú)法獲得全部無(wú)用類的郭蕉,因?yàn)闊o(wú)用的類里用了其他無(wú)用的類就算是有用了,所以需要進(jìn)行遞歸查找喂江,這樣才能夠連根拔起召锈。這個(gè)過(guò)程如果是手動(dòng)做比較費(fèi)勁、收益無(wú)法一次評(píng)估获询,很難推動(dòng)涨岁。同時(shí)還需要在線上灰度運(yùn)行時(shí)檢查實(shí)際類的使用情況,很多靜態(tài)層面關(guān)聯(lián)的類使用吉嚣,實(shí)際運(yùn)行過(guò)程中也可能用不到梢薪。
思路和關(guān)鍵代碼如下。
第一步
使用 MethodTraceAnalyze 里 ParseOC 類的 ocNodes 函數(shù)尝哆,通過(guò)傳入 workspace 路徑獲取所有節(jié)點(diǎn)的結(jié)構(gòu)體 OCNode沮尿。
let allNodes = ParseOC.ocNodes(workspacePath: workSpacePath)
找出類型是方法的結(jié)構(gòu)體,因?yàn)轭惖某跏蓟褪褂枚际窃谶@些方法中進(jìn)行的较解。OCNode 針對(duì)不同類型所存儲(chǔ)的數(shù)據(jù)也是不同的,所以我定義一個(gè) OCNodeValueProtocol 協(xié)議屬性赴邻,這樣就可以針對(duì)不同類型的節(jié)點(diǎn)存儲(chǔ)不同的數(shù)據(jù)印衔。
public struct OCNodeDefaultValue: OCNodeValueProtocol {
public var defaultValue: String
init() {
defaultValue = ""
}
}
public struct OCNodeMethod: OCNodeValueProtocol {
public var belongClass: String
public var methodName: String
public var tokenNodes: [OCTokenNode]
}
public struct OCNodeClass: OCNodeValueProtocol {
public var className: String
public var baseClass: String
public var hMethod: [String]
public var mMethod: [String]
public var baseClasses: [String]
}
可以看到對(duì)方法類型會(huì)存所屬類、方法名和方法內(nèi)所有 token以便進(jìn)行進(jìn)一步分析姥敛。對(duì)類這種類型會(huì)記錄他的基類奸焙、類名、頭文件方法列表和實(shí)現(xiàn)文件方法列表,還用一個(gè)棧記錄繼承鏈与帆。
第二步
獲取所有類的節(jié)點(diǎn)了赌,通過(guò)對(duì)方法內(nèi)所有 token 的分析來(lái)看使用了哪些類,并記錄使用的類玄糟。
獲取所有類節(jié)點(diǎn)的代碼如下:
// 獲取所有類節(jié)點(diǎn)
var allClassSet:Set<String> = Set()
for aNode in allNodes {
if aNode.type == .class {
let classValue = aNode.value as! OCNodeClass
allClassSet.insert(classValue.className)
if classValue.baseClass.count > 0 {
baseClasses.insert(classValue.baseClass)
}
}
} // end for aNode in allNodes
記錄使用的類關(guān)鍵代碼:
static func parseAMethodUsedClass(node: OCNode, allClass: Set<String>) -> Set<String> {
var usedClassSet:Set<String> = Set()
guard node.type == .method else {
return usedClassSet
}
let methodValue:OCNodeMethod = node.value as! OCNodeMethod
for aNode in methodValue.tokenNodes {
if allClass.contains(aNode.value) {
usedClassSet.insert(aNode.value)
}
}
return usedClassSet
}
第三步
有了所有使用的類和所有的類勿她,就能夠獲取沒(méi)用到的類。為了跑一次就能夠?qū)⑺袥](méi)用的類找出阵翎,所以需要在找到無(wú)用類后逢并,將這些類自動(dòng)去掉再進(jìn)行下一次查找。我這里寫了個(gè)遞歸來(lái)干這件事郭卫。具體代碼如下:
var recursiveCount = 0
func recursiveCheckUnUsedClass(unUsed:Set<String>) -> Set<String> {
recursiveCount += 1
print("into recursive!!!!第\(recursiveCount)次")
print("----------------------\n")
for a in unUsed {
print(a)
}
var unUsedClassSet = unUsed
// 縮小范圍
for aUnUsed in unUsedClassSet {
if allClassSet.contains(aUnUsed) {
allClassSet.remove(aUnUsed)
}
}
var allUsedClassSet:Set<String> = Set()
for aNode in allNodes {
if aNode.type == .method {
let nodeValue:OCNodeMethod = aNode.value as! OCNodeMethod
// 過(guò)濾已判定無(wú)用類里的方法
guard !unUsedClassSet.contains(nodeValue.belongClass) else {
continue
}
let usedSet = ParseOCMethodContent.parseAMethodUsedClass(node: aNode, allClass: allClassSet)
if usedSet.count > 0 {
for aSet in usedSet {
allUsedClassSet.insert(aSet)
}
} // end if usedSet.count > 0
} // end if aNode.type == .method
} // end for aNode in allNodes
var hasUnUsed = false
// 找出無(wú)用類
for aSet in allClassSet {
if !allUsedClassSet.contains(aSet) {
unUsedClassSet.insert(aSet)
hasUnUsed = true
}
}
if hasUnUsed {
// 如果發(fā)現(xiàn)還有無(wú)用的類乃摹,需要繼續(xù)遞歸調(diào)用進(jìn)行分析
return recursiveCheckUnUsedClass(unUsed: unUsedClassSet)
}
return unUsedClassSet
}
// 遞歸調(diào)用
var unUsedClassFromRecursive = recursiveCheckUnUsedClass(unUsed: Set<String>())
通過(guò)遞歸進(jìn)行多次能夠取到最終的結(jié)果陵像。
第四步
對(duì)于繼承和系統(tǒng)的類還需要進(jìn)行過(guò)濾,進(jìn)一步提高準(zhǔn)確性。
let unUsedClassSetCopy = unUsedClassFromRecursive
for aSet in unUsedClassSetCopy {
// 過(guò)濾系統(tǒng)控件
let filters = ["NS","UI"]
var shouldFilter = false
for filter in filters {
if aSet.hasPrefix(filter) {
shouldFilter = true
}
}
// 過(guò)濾基類
if baseClasses.contains(aSet) {
shouldFilter = true
}
// 開(kāi)始過(guò)濾
if shouldFilter {
unUsedClassFromRecursive.remove(aSet)
}
}
清理了通過(guò)這種靜態(tài)掃描出的無(wú)用類烁试,還可以通過(guò)運(yùn)行時(shí)來(lái)判斷類是否被初始化了,從而找出無(wú)用類饮怯。類運(yùn)行時(shí)是否初始化的這個(gè)信息是個(gè)布爾值废境,叫 isInitialized,存儲(chǔ)在元類 class_rw_t 結(jié)構(gòu)體的 flags 字段里寒跳,在 1<<29 位記錄聘萨。
完整代碼見(jiàn) ParseOCMethodContent 文件:MethodTraceAnalyze/ParseOCMethodContent.swift at master · ming1016/MethodTraceAnalyze · GitHub
在廣州做的 SwiftUI 學(xué)習(xí)筆記分享
下面是筆記內(nèi)容:
推薦喵神的 SwiftUI 新書(shū),ObjC 中國(guó) - SwiftUI 與 Combine 編程童太。
這本介紹 Combine 的書(shū)也介紹的非常詳細(xì):Using Combine
這個(gè)網(wǎng)站有大量 SwiftUI 的控件使用范例可以參考:SwiftUI by Example - free quick start tutorials for Swift developers
這個(gè)博客每篇都是 SwiftUI 相關(guān)的米辐,而且更新非常頻繁:Home | Majid’s blog about Swift development
InfoQ二叉樹(shù)視頻
五分鐘的視頻,在導(dǎo)演構(gòu)思下需要一天在四個(gè)地方進(jìn)行拍攝书释,由于前一天晚上慶功宴喝高了翘贮,拍攝當(dāng)天 iPad 筆也忘帶了,頭還有些懵爆惧。導(dǎo)演中午飯都沒(méi)吃專門回家拿了他的筆給我用狸页。下午地點(diǎn)安排在央美,先訪談再畫一張扯再。北京電影學(xué)院畢業(yè)14年專業(yè)繪畫經(jīng)驗(yàn)的導(dǎo)演賈成斌芍耘,在我畫時(shí)邊幫我改畫邊傳授了經(jīng)驗(yàn),我覺(jué)得這些經(jīng)驗(yàn)會(huì)讓我更進(jìn)一步熄阻。
下面是記者剡沛在 InfoQ 上發(fā)布的采訪內(nèi)容和視頻斋竞,原文在:“創(chuàng)造,就值得被肯定”秃殉,一名程序員的藝術(shù)人生丨二叉樹(shù)視頻
他是一名程序員坝初,同時(shí)也用自己的業(yè)余時(shí)間畫畫浸剩。無(wú)論是技術(shù)分享還是珍藏回憶,他都用畫筆記錄自己鳄袍,連接他人绢要。他覺(jué)得程序員很酷,無(wú)論編程還是畫畫拗小,都是在創(chuàng)造重罪,這就是最值得肯定的事情。
他在高德負(fù)責(zé)架構(gòu)研發(fā)工作十籍,也是大家眼中的藝術(shù)家蛆封,在他身上總能看到那些執(zhí)念與決心,它們發(fā)著光勾栗,無(wú)時(shí)無(wú)刻不影響著周圍的人惨篱。
他就是戴銘,一名酷酷的程序員围俘。
當(dāng)聊到”連接“這個(gè)詞的時(shí)候砸讳,他的眼神異常堅(jiān)定。
他覺(jué)得自己堅(jiān)持創(chuàng)作界牡,堅(jiān)持做很多沒(méi)有門檻的技術(shù)分享簿寂,很大一部分動(dòng)力就來(lái)自這種渴望,渴望連接自己的過(guò)去宿亡,也渴望連接他人常遂。
他就是戴銘,一個(gè)有點(diǎn)酷挽荠,還有點(diǎn)文藝的程序員克胳,在高德地圖負(fù)責(zé)架構(gòu)研發(fā)工作。除了把自己活的很年輕圈匆,在他身上總能看到一些發(fā)著光的東西漠另。
“是信念嗎?”
“是執(zhí)念跃赚“蚀辏”
“當(dāng)漫畫家,可能連飯都吃不飽纬傲÷埽”
故事的開(kāi)頭,多少有些遺憾叹括。
戴銘最早接觸畫畫葫录,是小學(xué)之前報(bào)過(guò)的一個(gè)高階國(guó)畫班,因?yàn)槔蠋熢谏虾A旎运慨嬐暌粡埗家倪^(guò)去并等待回信米同,當(dāng)其中一幅畫改到第三遍的時(shí)候老師回信說(shuō):這孩子可能沒(méi)什么天賦。因?yàn)檫@件事摔竿,當(dāng)時(shí)戴銘心里對(duì)畫畫的渴望面粮,幾乎降到了冰點(diǎn),對(duì)于畫畫的興趣也就此擱置继低。
直到六年級(jí)的一次美術(shù)作業(yè)熬苍,平時(shí)酷愛(ài)看漫畫的戴銘,才再一次下定決心把自己喜歡的角色搬到紙上袁翁。
“同學(xué)都說(shuō)畫的太像了柴底,那種被再次肯定的開(kāi)心,很難忘記粱胜”ぃ”
后來(lái)整個(gè)初中,戴銘都在課余時(shí)間畫漫畫焙压,也沒(méi)再報(bào)班鸿脓,一直到初中畢業(yè)戴銘跟父親說(shuō)不想上學(xué)了,“想去畫漫畫涯曲,做一名漫畫家”野哭。不難預(yù)料,這個(gè)想法并沒(méi)有得到父親的支持幻件,“當(dāng)漫畫家拨黔,可能連飯都吃不飽”。
但從戴銘的話語(yǔ)中绰沥,并沒(méi)有因?yàn)楦赣H這次選擇而聽(tīng)到絲毫氣餒篱蝇,似乎心里的種子已經(jīng)生根。
“興趣不是說(shuō)喜歡漫畫揪利,就要去從事漫畫态兴。當(dāng)我們從被動(dòng)的行為中獲得成就感時(shí),也會(huì)在無(wú)形中培養(yǎng)出興趣疟位,畫畫如此瞻润,編程也是一樣√鹂蹋“
“我不想讓自己投入了生命的熱愛(ài)绍撞,停滯不前〉迷海”
“從那后來(lái)傻铣,就一直在堅(jiān)持畫畫了∠榻剩”
從臨摹簡(jiǎn)單的漫畫非洲,到更復(fù)雜的畫風(fēng)鸭限、更多元的角色,再到臨摹寫實(shí)人物两踏、影視劇照败京,期間還專門自學(xué)過(guò)素描,直到現(xiàn)在的再創(chuàng)作梦染,戴銘除了把自己的愛(ài)好和回憶畫出來(lái)赡麦,還把自己的專業(yè)內(nèi)容做成漫畫,用更容易傳達(dá)的方式去做每一次技術(shù)分享帕识。
“大概是從四泛粹、五年前開(kāi)始,空閑的時(shí)候會(huì)花很長(zhǎng)的時(shí)間畫畫肮疗,平時(shí)每天也會(huì)擠出一個(gè)多小時(shí)堅(jiān)持練手晶姊,因?yàn)槲也幌M约簾釔?ài)的東西,停滯不前族吻,興趣不該只是興趣而已帽借。”
后來(lái)戴銘接觸了數(shù)繪超歌,就開(kāi)始把很多創(chuàng)作留在屏幕上砍艾。
“用 ipad 畫,更適合我現(xiàn)在的角色巍举,因?yàn)榭梢噪S時(shí)開(kāi)始和結(jié)束脆荷,不受環(huán)境和工具的影響,另一方面數(shù)字繪圖也讓他的作品在色彩方面懊悯,有了更多的提升空間蜓谋。”
從容地掏出平板炭分,只要自己想桃焕,就能隨時(shí)還原周遭的一切。這種感覺(jué)捧毛,就跟戴銘看待程序員時(shí)的表達(dá)一樣观堂,“都是很酷的事情,因?yàn)闊o(wú)論程序員的人還是藝術(shù)家呀忧,他們都在創(chuàng)造新的事物师痕,僅這一點(diǎn),就值得被肯定而账∫确兀”
“工作不用心的人,生活也不會(huì)太精彩”
類似畫畫這種需要大量時(shí)間去“熬”的愛(ài)好泞辐,堅(jiān)持總是最難的部分笔横。
“時(shí)間永遠(yuǎn)都是緊缺的竞滓,這是肯定。如果工作很忙吹缔,就先把時(shí)間全部投入到工作上虽界,用最快的速度做好、做完涛菠,才能有更多的精力和心情去做其他事情”。
戴銘上一份工作在滴滴撇吞,剛?cè)肼毦拖M貙捵约旱哪芰Ψ秶锥常瑤缀醭袚?dān)整個(gè)部門的研發(fā)任務(wù),后來(lái)臨近發(fā)版 Bug 實(shí)在解不完牍颈,第二天來(lái)公司發(fā)現(xiàn)都被領(lǐng)導(dǎo)默默解掉了迄薄,才意識(shí)到一個(gè)人的力量始終有限,“一個(gè)手指煮岁,肯定比不過(guò)一個(gè)拳頭的力量讥蔽。”
即便如此画机,戴銘也始終保持著對(duì)工作的熱血冶伞,當(dāng)時(shí)間不夠用的時(shí)候,他會(huì)換個(gè)角度去看待問(wèn)題步氏。
“我覺(jué)得工作上面不上心响禽、不拼命的人,生活也不會(huì)太精彩荚醒。工作是跟每個(gè)人的一生切實(shí)相關(guān)的事情芋类,如果連這個(gè)都做不好,又如何能在其他事情上更用心的經(jīng)營(yíng)界阁?”
時(shí)間看透了侯繁,剩下的就是堅(jiān)持。
當(dāng)聊到堅(jiān)持的原動(dòng)力時(shí)泡躯,除了用回憶和分享去推動(dòng)自己贮竟,在戴銘身上總散發(fā)著一股勁兒。一個(gè)興趣愛(ài)好而已精续,談信念可能過(guò)于悲壯坝锰,所以他認(rèn)為,這股勁兒更像是決心和執(zhí)念重付。
有剛?cè)脒M(jìn)入滴滴時(shí)顷级,想肩扛所有工作的執(zhí)念;
有為了興趣上一個(gè)臺(tái)階确垫,努力獲得央美朋友肯定的執(zhí)念弓颈;
也有怕自己的作品破壞了心中的完美角色帽芽,重復(fù)打磨的執(zhí)念。
結(jié)尾
戴銘是很強(qiáng)大的人翔冀,他講過(guò)的一段話令人印象深刻:
“幸福是面對(duì)過(guò)去导街,恐懼是面對(duì)未知的未來(lái)。我也忘記是從哪里看到的這句話纤子,但我自己會(huì)這樣理解:回憶是讓人幸福的搬瑰,未知是令人恐懼的。但如果我們沉湎在回憶中不敢面對(duì)未來(lái)控硼,幸福始終是有限的泽论,當(dāng)我們用決心去面對(duì)未來(lái),幸福就會(huì)越來(lái)越多卡乾,恐懼也會(huì)越來(lái)越少翼悴。”
裹著決心這把利劍幔妨,酷酷的戴銘就這樣用代碼和畫筆勾勒著自己的一生鹦赎,而畫卷展開(kāi)的部分就已經(jīng)足夠精彩,余下的误堡,定會(huì)更值得期待古话。