如果從runtime的objc_msgSend角度出發(fā),此種設(shè)計(jì)主要是為了復(fù)用消息機(jī)制,并提高消息發(fā)送效率。
oc在調(diào)用過(guò)程中,編譯后實(shí)際上是使用了objc_msgSend進(jìn)行消息發(fā)送。我們知道,實(shí)例方法(比如-(void)A號(hào)方法)屬性等,存儲(chǔ)位置在類(lèi)中。同時(shí)我們每一次初始化alloc一個(gè)實(shí)例對(duì)象后,此實(shí)例對(duì)象也都會(huì)默認(rèn)保存一個(gè)isa指針,指向類(lèi)。我們?cè)诰帉?xiě)代碼寫(xiě)的調(diào)用方法,在編譯完成后都會(huì)使用runtime編譯稱(chēng)此種形式
id objc_msgSend(id self, SEL op, ...)
這個(gè)函數(shù)有倆個(gè)隱式的參數(shù):消息的接收者,消息的?法名。通過(guò)這倆個(gè)參數(shù)就能去找到對(duì)應(yīng)?法的實(shí)現(xiàn)。所以如果正常情況下,編譯期間只接調(diào)用就可以,因?yàn)閕sa指針和參數(shù)名稱(chēng)在【A對(duì)象 A】這種中括號(hào)發(fā)送小時(shí)時(shí)候都能拿到。這樣看的話(huà),對(duì)于實(shí)例對(duì)象來(lái)說(shuō)并沒(méi)有什么影響。
但是,問(wèn)題來(lái)了,oc中類(lèi)方法+號(hào)方法和實(shí)例方法-號(hào)方法是可以同名的。比如項(xiàng)目中可以同時(shí)存在+A方法和-A方法。如果此種方式的話(huà),編譯后的objc_msgSend要怎么處理????解決方法就是增加的新參數(shù),標(biāo)記是類(lèi)方法還是實(shí)例方法。還需要一個(gè)參數(shù)標(biāo)記接受消息的對(duì)象是實(shí)例對(duì)象還是類(lèi)對(duì)象。
顯然是不可能的,objc_msgSend方法作為oc這門(mén)語(yǔ)言的設(shè)計(jì)基礎(chǔ),消息發(fā)送效率肯定是越快越好。如果沒(méi)有元類(lèi)主動(dòng)區(qū)分開(kāi),在運(yùn)行時(shí),在objc_msgSend這個(gè)重度使用方法邏輯中去做大量的其他區(qū)分,無(wú)疑是對(duì)運(yùn)行效率的大量浪費(fèi)。
所以元類(lèi)的出現(xiàn)就解決了這個(gè)問(wèn)題,讓各類(lèi)各司其職,實(shí)例對(duì)象就?存儲(chǔ)屬性值的事,類(lèi)對(duì)象存儲(chǔ)實(shí)例?法列表,元類(lèi)對(duì)象存儲(chǔ)類(lèi)?法列表,符合設(shè)計(jì)原則中的單?職責(zé),?且忽略了對(duì)對(duì)象類(lèi)型的判斷和?法類(lèi)型的判斷可以??的提升消息發(fā)送的效率,并且在不同種類(lèi)的?法?的都是同?套流程,在之后的維護(hù)上也??節(jié)約了成本。
此外在objc底層沒(méi)有類(lèi)?法和實(shí)例?法的區(qū)別,都是函數(shù)。
我們可以通過(guò)runtime中提供的函數(shù)證明這一點(diǎn)

然后我們看下源碼中class_getInstanceMethod的方法實(shí)現(xiàn)

從此處我們也可以看到,類(lèi)方法在元類(lèi)中的存儲(chǔ)方式和實(shí)例方法在類(lèi)中的存儲(chǔ)方法,并無(wú)二致。針對(duì)于runtime獲取方式來(lái)說(shuō),類(lèi)方法和實(shí)例方法其實(shí)都是函數(shù),只是保存的位置有所區(qū)別。