《iOS開發(fā)一起進(jìn)大廠》系列之——美團(tuán)面試(系列持續(xù)更新)

學(xué)而時(shí)習(xí)之,不亦說乎

原諒我,我是個(gè)標(biāo)題黨,所有文章的名字只是我的噱頭,偉大的喬布斯告訴我們"Stay hungry,Stay foolish",希望大家有空杯心態(tài) ,一起學(xué)習(xí),一起進(jìn)步。

分類Category 我想絕大部分人應(yīng)該不陌生,就算自己沒寫過分類Category ,一些知名的三方庫里都會(huì)用到。 而且這也是各一線大廠面試出現(xiàn)頻率很高的題 。

上一篇文章,有人反饋說,文章提到的問題,是最基礎(chǔ)的。意思是還要深挖?更多深底層? 我知道人的精力是有限的,每個(gè)人準(zhǔn)備面試時(shí)間也是有限的,在有限的時(shí)間里,最大限度的提高復(fù)習(xí)效率,這才是正道。 我也盡量在文中體現(xiàn)適當(dāng)?shù)脑?,源碼細(xì)節(jié)。

不禁感慨想起一句話: 「面試造火箭,入職擰螺絲」。 不過小白們也不要憤憤不平。 畢竟好崗位競爭激烈,只有面試造火箭才能找出出更優(yōu)秀人。不然大家都考100分,那怎么區(qū)分出誰更厲害。

開始面試

我正在會(huì)議室略有緊張的等待面試,忽然看到一個(gè)穿著格子襯衫,大腹便便的中年男子拿著簡歷向我走來, 我看著他頭上快要絕頂?shù)念^發(fā),心想這肯定是個(gè)iOS開發(fā)技術(shù)牛逼閃閃的老前輩。

還好看過杯子寫《iOS之一起進(jìn)大廠》系列,想想現(xiàn)在是滿腹經(jīng)綸,剛緊張到提到嗓子眼的心,又按下去了,淡定從容,一點(diǎn)都不虛好伐,就是這么自信淡定。

我什么時(shí)候也能變成那樣厲害的高手

1. 小伙子,你家里里提到用過Category,那你可以說他的使用場景是什么,用途是什么?

帥氣逼人的面試官您好,Category的用戶可以歸納為以下幾點(diǎn):

  1. 給現(xiàn)有類添加方法,豐富現(xiàn)有類的功能。 比如有的人就為NSString這類添加了一些很實(shí)用的方法(判斷字符串是否郵箱,轉(zhuǎn)化字符串為MD5)

  2. 分解代碼龐大功能復(fù)雜的類。把功能復(fù)雜代碼很多的類,可以按照不同功能的做分類,同一功能放到一個(gè)文件里,體現(xiàn)單一職責(zé)原則。

  3. 聲明私有方法。比如定義一個(gè)分類,只有頭文件放到對(duì)應(yīng)宿主.m里,滿足私有方法的聲明和使用,不暴露具體實(shí)現(xiàn)。

還有其他用法, 但是蘋果不歡迎這樣用法: 把系統(tǒng)Framework的私有方法公開化。

  1. 那你說說Category的底層實(shí)現(xiàn)是什么?

熟悉oc底層的同學(xué)知道,在runtime層都是類和對(duì)象都是 struct表示的,category也不例外,category用結(jié)構(gòu)體category_t,結(jié)構(gòu)體包含:

  1. 類的名字(name)

  2. 類(cls)

  3. category中所有給類添加的實(shí)例方法的列表(instanceMethods)

  4. category中所有添加的類方法的列表(classMethods)

  5. category實(shí)現(xiàn)的所有協(xié)議的列表(protocols)

  6. category中添加的所有屬性(instanceProperties)

typedef struct category_t {
    const char *name;
    classref_t cls;
    struct method_list_t *instanceMethods;
    struct method_list_t *classMethods;
    struct protocol_list_t *protocols;
    struct property_list_t *instanceProperties;
} category_t;

從category的定義也可以看出category的可為(可以添加實(shí)例方法,類方法,甚至可以實(shí)現(xiàn)協(xié)議,添加屬性)。 category成員變量列表是只讀,所以category不能添加實(shí)例變量。

  1. 小伙子不錯(cuò),那你再說說 Category的特點(diǎn)有什么?和 Class Extendsion(類擴(kuò)展)有什么區(qū)別?

帥氣的面試官,這個(gè)不難。 我先說下特點(diǎn)。

  1. 「分類的特點(diǎn)」

分類是運(yùn)行時(shí)決議。怎么理解運(yùn)行時(shí)決議呢? 編譯好的分類的文件,是沒把相應(yīng)分類的內(nèi)容加到宿主類上的。只有在運(yùn)行時(shí)Runtime 才把分類的內(nèi)容添加到宿主類上。

  1. 「類擴(kuò)展的用途是什么?」

一般把不想對(duì)外公開一些類的方法,屬性,成員變量的時(shí)候可以用類的擴(kuò)展 類擴(kuò)展代碼格式:

@interface XXX ()
//私有屬性
//私有方法(如果不實(shí)現(xiàn),編譯時(shí)會(huì)報(bào)警,Method definition for 'XXX' not found)
@end
  1. 那咱們?cè)倭纳钜稽c(diǎn)的,Category中+load 和+initialize調(diào)用的順序是什么樣的? Category的+load 和+initialize 方法的區(qū)別什么?

「調(diào)用順序:」

  1. 類要優(yōu)先于分類調(diào)用+load方法。

  2. 先編譯的分類的+load方法會(huì)被優(yōu)先調(diào)。

  3. 父類+load優(yōu)先于子類。

  4. 由于分類是objc_msgSend機(jī)制,+initialize在所有分類要優(yōu)先于類調(diào)用。 在類中 +initialize父類優(yōu)先于子類 。如果子類沒有實(shí)現(xiàn)+initialize,會(huì)調(diào)用父類的+initialize(所以同一個(gè)父類的+initialize可能會(huì)被調(diào)用多次)。

  5. 「是什么決定了哪個(gè)先編譯呢?如下圖Category1的+load比Category2的+load優(yōu)先調(diào)用。如果編譯文件順序換一下,被調(diào)用的順序也跟著變?!?/h6>

「+load 和+initialize 區(qū)別在于調(diào)用方式和調(diào)用時(shí)刻不同」

  1. 調(diào)用方式不同:+load是根據(jù)函數(shù)地址直接調(diào)用,initialize是通過objc_msgSend調(diào)用

  2. 調(diào)用時(shí)刻不同:+load方法會(huì)在runtime加載類、分類時(shí)調(diào)用(而且程序運(yùn)行過程中只調(diào)用一次)。
    +initialize是類第一次接收到消息的時(shí)候調(diào)用(即是類調(diào)用alloc時(shí)),每一個(gè)類只會(huì)initialize一次(上面提到子類沒有實(shí)現(xiàn)+initialize,會(huì)調(diào)用父類的+initialize。這樣父類的initialize方法可能會(huì)被調(diào)用多次)

  3. 那怎么理解category方法覆蓋的問題?

「分類添加的方法可以“覆蓋”原類方法」

其實(shí)覆蓋沒有真正的覆蓋。如果在category里添加了methodA,那么原類的methodA也是存在的,沒有真正覆蓋掉。只是系統(tǒng)只會(huì)調(diào)用后來category添加的 methodA。

因?yàn)閏ategory的methodA被放到了方法列表的前面,原來類中methodA被放到了方法列表的后面,在運(yùn)行時(shí)查找方法會(huì)先找到了category 添加的同名方法,就產(chǎn)生了“覆蓋“原來類的同名方法的效果。

如果多個(gè)分類有同名方法,那么誰能生效取決于誰最后參與編譯。最后參與編譯的分類對(duì)應(yīng)的方法就會(huì)生效。

  1. 能否給category添加實(shí)例變量?那如何給分類添加實(shí)例變量?

「不能?!?從上面提到的分類的底層實(shí)現(xiàn),category成員變量列表是只讀,所以category不能添加實(shí)例變
量。

「那怎么添加呢? 直接添加肯定是不行,可以間接的添加.」

  1. 思路1 分類添加全局變量,并且手動(dòng)重寫setter/getter方法實(shí)現(xiàn)。但是全局變量有很多隱患,對(duì)象銷毀時(shí)無法銷毀。不建議采用。

  2. 思路2 用關(guān)聯(lián)對(duì)象方法添加實(shí)例變量。通過runtime提供的關(guān)聯(lián)對(duì)象的方法可以簡潔的給分類添加成員變量。通過下面提供的關(guān)聯(lián)對(duì)象下API,可以實(shí)現(xiàn)給分類添加實(shí)例變量:

添加關(guān)聯(lián)對(duì)象
void objc_setAssociatedObject(id object, const void * key, id value, objc_AssociationPolicy policy)
獲得關(guān)聯(lián)對(duì)象
id objc_getAssociatedObject(id object, const void * key)
移除所有的關(guān)聯(lián)對(duì)象
void objc_removeAssociatedObjects(id object)

「簡單說下關(guān)聯(lián)對(duì)象方法添加實(shí)例變量原理:」

其實(shí)關(guān)聯(lián)對(duì)象并沒有添加到實(shí)例變量到被關(guān)聯(lián)對(duì)象內(nèi)存中, 關(guān)聯(lián)對(duì)象有一個(gè)全局內(nèi)容管理器(「AssociationsManager」),關(guān)聯(lián)對(duì)象通過上面提到的三個(gè)API,操作實(shí)例變量 存取,移除。從而完成了對(duì)分類的添加實(shí)例變量。

「面試結(jié)束」

小伙子回答的不錯(cuò),很對(duì)我口味,記得明天再來,明天還有更精彩的面試其他內(nèi)容。 我的內(nèi)心OS:天吶嚕,明天還有!! 這是要在走禿(走向禿頂?shù)牡缆? 越走越遠(yuǎn)啊。 (為了下一篇文章,強(qiáng)行做引子,哈哈)

持續(xù)更新--請(qǐng)iOS的小伙伴關(guān)注! 喜歡的話給一個(gè)贊吧!

作為一個(gè)開發(fā)者,有一個(gè)學(xué)習(xí)的氛圍跟一個(gè)交流圈子特別重要,這是一個(gè)我的iOS技術(shù)圈子:1001906160 ,進(jìn)群密碼000,不管你是小白還是大牛歡迎入駐 ,分享BAT,阿里面試題、面試經(jīng)驗(yàn),討論技術(shù), 大家一起交流學(xué)習(xí)成長!

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

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