Category為何不能添加成員變量?

關(guān)于Category的原理,參考這篇文章:http://tech.meituan.com/DiveIntoCategory.html

Category的使用場景

  • 可以把類的實現(xiàn)分開在幾個不同的文件里面。這樣做有幾個顯而易見的好處:
    a)可以減少單個文件的體積
    b)可以把不同的功能組織到不同的category里
    c)可以由多個開發(fā)者共同完成一個類
    d)可以按需加載想要的category
  • 聲明私有方法
  • 模擬多繼承
  • 把framework的私有方法公開

Category到底是不能添加成員變量,還是屬性?還是都不能添加?

屬性和成員變量的區(qū)別,就不介紹了。
首先,Category在代碼里添加成員變量是根本編譯不過去的,而添加屬性是可以編譯通過的,如圖:


FFA05734-5479-4D2F-AC71-78C86C681EC7.png

但是,在Category添加的屬性后,只會聲明setter和getter方法,.m文件并未實現(xiàn)setter和getter方法,會有黃色預(yù)警,如圖:


4F9D5BD7-FB97-49BE-9B35-7FC274A4D062.png

而且,如果調(diào)用的話,也不會被賦值,如圖:


035F002C-E200-4F7D-BDF1-EFB56C117583.png

最常見的解決方案就是用runtime手動實現(xiàn)setter和getter方法,這里就不介紹了。
所以,剛才那個問題,Category不能添加成員變量和屬性?就可以有答案了,不能添加成員變量,可以添加屬性,但是屬性要手動實現(xiàn)setter和getter方法。

Category為何不能添加成員變量,而只能添加方法?

這要從Category的原理說起,簡單地說就是通過runtime動態(tài)地把Category中的方法等添加到類中(蘋果在實現(xiàn)的過程中并未將屬性添加到類中,所以屬性僅僅是聲明了setter和getter方法,而并未實現(xiàn)),具體請參考http://tech.meituan.com/DiveIntoCategory.html
Objective-C提供的runtime函數(shù)中,確實有一個lass_addIvar()
函數(shù)用于給類添加成員變量,但是文檔中特別說明:

This function may only be called after objc_allocateClassPair and before objc_registerClassPair. Adding an instance variable to an existing class is not supported.

意思是說,這個函數(shù)只能在“構(gòu)建一個類的過程中”調(diào)用。一旦完成類定義,就不能再添加成員變量了。經(jīng)過編譯的類在程序啟動后就被runtime加載,沒有機會調(diào)用addIvar。程序在運行時動態(tài)構(gòu)建的類需要在調(diào)用objc_registerClassPair
之后才可以被使用,同樣沒有機會再添加成員變量。
我們設(shè)想一下如果Objective-C允許動態(tài)增加成員變量,會發(fā)生什么事情。假設(shè)如下代碼可以執(zhí)行。

E6AD8A2E-77C0-4186-AFC0-26C8451F6571.png
MyObject *obj = [[MyObject alloc] init];
// 基類增加一個4字節(jié)的成員變量someVar
class_addIvar([NSObject class], "someVar", 4, ...);
// 基類增加方法someMethod,用到了someVar
class_addMethod([NSObject class], @selector(someMethod), ...);
// 調(diào)用someMethod,修改了someVar
[obj someMethod];
// 訪問子類成員變量,會發(fā)生什么?
[obj->students length];

顯然,這樣做會帶來嚴重問題,為基類動態(tài)增加成員變量會導(dǎo)致所有已創(chuàng)建出的子類實例都無法使用,比如上線后的app,如果用戶手機系統(tǒng)升級iOS新版本后,必須重新編譯提交才能在新版系統(tǒng)上運行。那為什么runtime允許動態(tài)添加方法和屬性,而不會引發(fā)問題呢?

因為方法和屬性并不“屬于”類實例,而成員變量“屬于”類實例。我們所說的“類實例”概念,指的是一塊內(nèi)存區(qū)域,包含了isa指針和所有的成員變量。所以假如允許動態(tài)修改類成員變量布局,已經(jīng)創(chuàng)建出的類實例就不符合類定義了,變成了無效對象。但方法定義是在objc_class中管理的,不管如何增刪類方法,都不影響類實例的內(nèi)存布局,已經(jīng)創(chuàng)建出的類實例仍然可正常使用。

需要注意的有兩點:

  • 1)、category的方法沒有“完全替換掉”原來類已經(jīng)有的方法,也就是說如果category和原來類都有methodA,那么category附加完成之后,類的方法列表里會有兩個methodA
  • 2)、category的方法被放到了新方法列表的前面,而原來類的方法被放到了新方法列表的后面,這也就是我們平常所說的category的方法會“覆蓋”掉原來類的同名方法,這是因為運行時在查找方法的時候是順著方法列表的順序查找的,它只要一找到對應(yīng)名字的方法,就會罷休_,殊不知后面可能還有一樣名字的方法。

參考:http://quotation.github.io/objc/2015/05/21/objc-runtime-ivar-access.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,041評論 0 9
  • 今天朋友問我:我真佩服你,在一家公司這么長時間。 “你過來你也會這樣的,因為我們都有好奇心” 我13年畢業(yè)后進公司...
    呀呀丙閱讀 362評論 0 1
  • 夸人,講故事,提問都要注意細節(jié),具體到某個部分,不要太籠統(tǒng)。
    kwfs筆記閱讀 224評論 0 0

友情鏈接更多精彩內(nèi)容