WWDC2020介紹Objective-C 運行時的改進,主要介紹了三個變化:
1.Class data structures changes;
首先是數(shù)據(jù)結(jié)構(gòu)的變化,Objective-C 運行時會使用他們來追蹤類。
2.Relative method lists;
Objective-C方法列表的變化
3.tagged pointer 格式的變化;
本文先對Class data structures changes展開講解:
在磁盤上彪薛,在你的app二進制文件中 類時這樣的:
首先這個類對象本身,包含了最常被訪問的信息指向
元類(Metaclass)租副、超類(Superclass)和方法緩存的指針(Method cache)栋操。
它還有一個指向更多數(shù)據(jù)的指針,存儲額外信息的地方叫做class_ro_t, ro代表只讀茬高,它包括像類名稱,方法假抄、協(xié)議和實例變量的信息怎栽。
Swift 類和Objective-C類共享這一基礎(chǔ)結(jié)構(gòu),所以每個Swift類也有這些數(shù)據(jù)結(jié)構(gòu)宿饱,當(dāng)類第一次從磁盤加載到內(nèi)存中時婚瓜,他們一開始也是這樣的,但是一經(jīng)使用刑棵,他們就會發(fā)生變化巴刻。
這里先區(qū)分下:clean memory 和dirty memory
clean memory:是指加載后不會發(fā)生更改的內(nèi)存。class_ro_t 就屬于clean memory因為它是屬于只讀的蛉签。
dirty memory:是指在進程運行時會發(fā)生更改的內(nèi)存胡陪。
類結(jié)構(gòu)一經(jīng)使用就會變成dirty memory,因為運行時會向它寫入新的數(shù)據(jù)碍舍。
例如:創(chuàng)建一個新的方法緩存并從類中指向它柠座,dirty memory 比clean memory要昂貴得多,只要進程在運行片橡,它就一直存在妈经。
另一方面clean memory 可以進行移除從而節(jié)省更多的內(nèi)存空間,因為如果你需要clean memory系統(tǒng)可以從磁盤中重新加載捧书,MacOS 可以選擇換出dirty memory,但是因為iOS不使用swap吹泡,所以dirty memory在iOS中代價很大,dirty memory是這個類數(shù)據(jù)被分成兩部分的原因经瓷,可以保持清潔的數(shù)據(jù)越多越好爆哑,通過分離那些永遠不會更改的數(shù)據(jù),可以把大部分的類數(shù)據(jù)存儲為clean memory舆吮。
這些數(shù)據(jù)足以讓我們開始揭朝,但是運行時需要追蹤每個類的更多信息队贱,所以當(dāng)一個類首次被使用,運行時會為它分配額外的存儲容量潭袱,這個運行時分配的存儲容量是class_rw_t 用于讀取-編寫數(shù)據(jù)柱嫌,在這個數(shù)據(jù)結(jié)構(gòu)中,我們存儲了只有在運行時才會生成的新信息屯换。
例如:所有的類都會鏈接成一個樹狀結(jié)構(gòu)编丘,這是通過使用First Subclass 和 Next sibling Class 指針實現(xiàn)的,這運行時遍歷當(dāng)前使用的所有類趟径,對于使方法緩存無效非常有用瘪吏。
但為什么方法和屬性也在只讀數(shù)據(jù)中時這里還有方法和屬性呢癣防?
因為它們可以在運行時進行更改蜗巧。
當(dāng)category被加載時它可以向類中添加新的方法,而且開發(fā)者可以使用運行時API動態(tài)地添加他們蕾盯,因為class_ro_t是只讀的幕屹,所以我們需要在class_rw_t中追蹤這些東西。
現(xiàn)在這樣做會占用相當(dāng)多的內(nèi)存级遭,在任何給定的設(shè)備中都有許多類在使用望拖。我們在iPhone上的整個系統(tǒng)中測量了,大約30兆字節(jié)這些class_rw_t結(jié)構(gòu)挫鸽,那么如何縮小這些數(shù)據(jù)結(jié)構(gòu)呢说敏?
在讀取-編寫部分需要這些東西,因為他們可以喜愛運行時進行更改丢郊,但是通過檢查實際設(shè)備上的使用情況盔沫,我們發(fā)現(xiàn)大約只有10%的類真正地更改了他們的方法,只有Swift類會使用這個demangled name字段,但是Swift類并不需要這一字段枫匾,除非有東西詢問它們的 Objective-C名稱時才需要
所以架诞,我們可以拆掉那些平時不用的部分,這將class_rw_t的大小減少了一半
對于確實需要額外信息的類干茉,我們可以分配這些擴展記錄中的一個谴忧,并把它滑到類中供其使用。
大約90%的類從不需要這些擴展數(shù)據(jù)角虫,這在系統(tǒng)范圍內(nèi)可以節(jié)約大約14MB的內(nèi)存沾谓,這些內(nèi)存現(xiàn)在可以用于更有效的用途,比如存儲你的APP的數(shù)據(jù)戳鹅,因此 實際上你可以在你的Mac上看到這一變化帶來的影響搏屑,這只需要在終端機上運行一些簡單的命令,現(xiàn)在一起來看一下粉楚。
在此進入Macbook的終端辣恋,要運行一個命令它在任何Mac上都可用叫做heap,它還允許你檢查正在運行的進程所使用的堆內(nèi)存亮垫,將在Mac中的Mail app上運行它 ,現(xiàn)在 如果我運行該命令伟骨,他會輸出千行饮潦,顯示通過郵件進行的每個堆分配,所以 我只是要 grep 它為 我們今天一直在談?wù)摰念愋?class_rw_t類型 我還需要查詢標(biāo)頭 從返回的結(jié)果中,
可以看到 在郵件app中使用了大約9000個這樣的class_rw_t類型哨查,但是其中大約十分之一 900 多一點實際上需要使用這一擴展信息溺健,所以可以很容易地計算出通過這個改變所有節(jié)省的內(nèi)存,這是大小減半的類型稀并,所以如果我們從這個數(shù)字中減去,必須分配給擴展類型的內(nèi)存量单默,可以看到節(jié)省了大約1兆字節(jié)數(shù)據(jù)的四分之一碘举。
如果在系統(tǒng)范圍內(nèi)進行擴展,對dirty memory而言 這是真正的節(jié)省內(nèi)存搁廓。
現(xiàn)在很多從類獲取數(shù)據(jù)的代碼引颈,必須同時處理那些有擴展數(shù)據(jù)和沒有擴展數(shù)據(jù)的類,運行時會為你處理這一切境蜕,并且從外部看 一切都像往常一樣工作蝙场,只是使用了更少內(nèi)存,之所以會這樣粱年,是因為讀取這些結(jié)構(gòu)的代碼售滤,都在運行時內(nèi)并且還會同時進行更新.
堅持使用這些API真的很重要
因為試圖直接訪問這些數(shù)據(jù)的結(jié)構(gòu)的代碼,都將在今年的OS版本中停止工作台诗,舊東西已經(jīng)發(fā)生了變化完箩,而且該代碼不知道新的布局,看到一些真實的代碼由于這些變化而崩潰拉庶,除了你自己的代碼之外嗜憔,還要注意哪些外部依賴性,可能正把他們帶入到你的APP中氏仗,他們可能會在你沒有意識的情況下挖掘這些數(shù)據(jù)結(jié)構(gòu)吉捶,
這些結(jié)構(gòu)中的所有信息都可以通過官方API獲取,有一些函數(shù) 如class_getName和class_getSuperclass當(dāng)你使用這些API訪問信息時,你知道 無論我們在后臺進行什么更改皆尔,他們都將繼續(xù)工作呐舔,所有的API都可以在Objective-C運行時說明文檔中找到。
以上就是WWDC2020介紹Objective-C 運行時的改進之?dāng)?shù)據(jù)結(jié)構(gòu)的變化的一些記錄慷蠕,希望對大家的學(xué)習(xí)有所幫助珊拼。