《招一個(gè)靠譜的iOS》16-20

本人參考GitHub《招聘一個(gè)靠譜的iOS》面試題參考答案(上)
16. @synthesize合成實(shí)例變量的規(guī)則是什么?假如property名為foo,存在一個(gè)名為_foo的實(shí)例變量,那么還會(huì)自動(dòng)合成新變量么?
17. 在有了自動(dòng)合成屬性實(shí)例變量之后,@synthesize還有哪些使用場景?
18. objc中向一個(gè)nil對(duì)象發(fā)送消息將會(huì)發(fā)生什么?
19. objc中向一個(gè)對(duì)象發(fā)送消息[obj foo]和objc_msgSend()函數(shù)之間有什么關(guān)系?
20. 什么時(shí)候會(huì)報(bào)unrecognized selector的異常?

16. @synthesize合成實(shí)例變量的規(guī)則是什么?假如property名為foo,存在一個(gè)名為_foo的實(shí)例變量,那么還會(huì)自動(dòng)合成新變量么?

科普一個(gè)概念:
實(shí)例變量 = 成員變量 = ivar
如果使用了屬性的話,那么編譯器就會(huì)自動(dòng)編寫訪問屬性所需的方法,此過程叫做“自動(dòng)合成”(auto synthesis)。這個(gè)過程由編譯器在編譯期執(zhí)行,除了生成方法代碼之外,編譯器還要自動(dòng)向類中添加適當(dāng)類型的實(shí)例變量,并且在屬性名前面加下劃線,以此作為實(shí)例變量的名字。

@interface Person : NSObject
@property NSString *firstName;
@property NSString *lastName;
@end

在上例中,會(huì)生成兩個(gè)實(shí)例變量,其名稱分別為_firstName和_lastName。也可以在類的實(shí)現(xiàn)代碼中通過@synthesize語法來指定實(shí)例變量的名字。

@implementation
@synthesize firstName = _myFirstName;
@synthesize lastName = _myLastName;
@end

上述語法會(huì)將生成的實(shí)例變量命名為_myFirstName和_myLastName,而不再是默認(rèn)的_firstName和_lastName。
總結(jié)一下,@synthesize合成實(shí)例變量的規(guī)則,有以下幾點(diǎn):

  1. 如果沒有指定成員變量的名稱就會(huì)自動(dòng)生成一個(gè)屬性同名的加_的成員變量;
    即,@synthesize foo;則生成名稱為_foo的成員變量。
  2. 如果指定了成員變量的名稱,會(huì)生成一個(gè)指定名稱的成員變量;
    即,'@synthesize foo = _myFoo'則生成一個(gè)名稱為_myFoo的成員變量。
  3. 如果這個(gè)成員變量已經(jīng)存在了,就不再生成新的變量了;
@interface Person : NSObject
{
    NSString *_firstName;
}
@property (nonatomic, copy) NSString *firstName;

此時(shí),由于已存在名為_firstName的成員變量,就不再為firstName屬性合成新的成員變量了。

17. 在有了自動(dòng)合成屬性實(shí)例變量后,@synthesize還有哪些使用場景?

在回答這個(gè)問題前,先搞清楚一個(gè)問題:什么時(shí)候不會(huì)autosynthesis(自動(dòng)合成),即不自動(dòng)生成成員變量?
1.同時(shí)重寫了setter和getter時(shí);
2.重寫了只讀屬性的getter時(shí);
3.使用了@dynamic時(shí);
4.@protocol中定義的所有屬性;
5.在category中定義的所有屬性;
6.重載的屬性。
所以,使用@synthesize場景:
1.同時(shí)重寫了setter和getter時(shí),系統(tǒng)不再自動(dòng)合成,需使用@synthesize合成;
2.重寫了只讀屬性的getter時(shí),系統(tǒng)不再自動(dòng)合成,需使用@synthesize合成;
3.不使用默認(rèn)名稱的實(shí)例變量進(jìn)行合成,而是使用自定義名稱的成員變量進(jìn)行合成;
4.遵守協(xié)議的類,實(shí)現(xiàn)協(xié)議中的成員變量。

18. objc中向一個(gè)nil對(duì)象發(fā)送消息將會(huì)發(fā)生什么?

在Objective-C中向nil發(fā)送消息是完全有效的,只是在運(yùn)行時(shí)不會(huì)有任何作用:
Person *mother = [[Person spouse] mother];
如果spouse對(duì)象是nil,那么發(fā)送給nil(spouse)的消息mother也將放回nil。
向nil發(fā)送消息分為以下幾種情況:

  1. 如果一個(gè)方法返回值是一個(gè)對(duì)象,那么發(fā)送給nil的消息將返回0(nil);
  2. 如果方法返回值為指針類型,其指針大小為小于或者等于sizeof(void *),float,double,long double或者long long的整型標(biāo)量,發(fā)送給nil的消息將返回0;
  3. 如果方法返回值為結(jié)構(gòu)體,發(fā)送給nil的消息將返回0。結(jié)構(gòu)體中各個(gè)字段的值都將是0;
  4. 如果方法的返回值不是上述提到的幾種情況,那么發(fā)送給nil的消息的返回值將是未定義的。
    類在runtime中的源代碼:
struct objc_class
{
    Class isa OBJC_ISA_AVAILABILITY; // 類的isa指針指向Meta Class,因?yàn)镺bjc的類的本身也是一個(gè)Object,為了處理這個(gè)關(guān)系,runtime就創(chuàng)造了Meta Class,當(dāng)給類發(fā)送[NSObject alloc]這樣的消息時(shí),實(shí)際上是把這個(gè)消息發(fā)給了Class Object
    #if !__OBJC2__
    Class super_class OBJC2_UNAVAILABLE; // 父類
    const char *name OBJC2_UNAVAILABLE; // 類名
    long version OBJC2_UNAVAILABLE; // 類的版本信息,默認(rèn)為0
    long info OBJC2_UNAVAILABLE; // 類信息,供運(yùn)行期使用的一些位標(biāo)識(shí)
    long instance_size OBJC2_UNAVAILABLE; // 該類的實(shí)例變量大小
    struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; // 該類的成員變量鏈表
    struct objc_method_list **methodLists OBJC2_UNAVAILABLE; // 方法定義的鏈表
    struct objc_cache *cache OBJC_UNAVAILABLE; // 方法緩存,對(duì)象接到一個(gè)消息會(huì)根據(jù)isa指針查找消息對(duì)象,這時(shí)會(huì)在method Lists中遍歷,如果cache了,常用的方法調(diào)用時(shí)就能夠提高調(diào)用的效率
    struct objc_protocol_list *portocols OBJC2_UNAVAILABLE;// 協(xié)議鏈表
    #endif
} OBJC2_UNAVAILABLE;

Objective-C在向一個(gè)對(duì)象發(fā)送消息時(shí),runtime庫會(huì)根據(jù)對(duì)象的isa指針找到該對(duì)象實(shí)際所屬的類,然后在該類中的方法列表以及其父類方法列表中尋找該方法進(jìn)行運(yùn)行。在發(fā)送消息的時(shí)候,objc_msgSend方法不會(huì)返回值,所謂的返回內(nèi)容都是具體調(diào)用時(shí)執(zhí)行的。如果向一個(gè)nil對(duì)象發(fā)送消息,首先在尋找對(duì)象的isa指針時(shí)就是0地址返回了,所以不會(huì)出現(xiàn)任何錯(cuò)誤。

19. objc中向一個(gè)對(duì)象發(fā)送消息[obj foo]和objc_msgSend()函數(shù)之間有什么關(guān)系?

[objc foo]在編譯之后就是objc_msgSend()函數(shù)調(diào)用。

20. 什么時(shí)候會(huì)報(bào)unrecognized selector異常?

當(dāng)調(diào)用該對(duì)象的某個(gè)方法,而該對(duì)象沒有實(shí)現(xiàn)這個(gè)方法的時(shí)候會(huì)報(bào)unrecognized selector異常。
Objective-C是動(dòng)態(tài)語言,每個(gè)方法在運(yùn)行時(shí)會(huì)被動(dòng)態(tài)轉(zhuǎn)為消息發(fā)送,即:Objc_msgSend(receiver, selector);
Objective-C在向一個(gè)對(duì)象發(fā)送消息時(shí),runtime會(huì)根據(jù)對(duì)象的isa指針找到該對(duì)象實(shí)際所屬的類,然后在該類中的方法列表以及其父類的方法列表中尋找該方法運(yùn)行。如果,在最頂層的父類中依然找不到相應(yīng)的方法,程序會(huì)在運(yùn)行時(shí)掛掉并拋出異常unrecognized selector sent to XXX。但是在這之前,Objective-C的運(yùn)行時(shí)有三次拯救程序崩潰的機(jī)會(huì)。

  1. Method resolution(方法解析)
    Objective-C運(yùn)行時(shí)會(huì)調(diào)用+ (BOOL)resolveInstanceMethod:(SEL)sel或者+ (BOOL)resolveClassMethod:(SEL)sel,讓開發(fā)者有機(jī)會(huì)提供一個(gè)函數(shù)實(shí)現(xiàn)。如果開發(fā)者添加了函數(shù),那運(yùn)行時(shí)系統(tǒng)就會(huì)重新啟動(dòng)一次消息發(fā)送的過程,否則,就會(huì)進(jìn)行下一步,消息轉(zhuǎn)發(fā)(Method Forwarding)。
  2. Fast Forwarding
    如果目標(biāo)對(duì)象實(shí)現(xiàn)了- (id)forwardingTargetForSelector:(SEL)aSelector,Runtime這時(shí)就會(huì)調(diào)用這個(gè)方法,把這個(gè)消息轉(zhuǎn)發(fā)給其他對(duì)象處理,只要這個(gè)方法的返回不是nil或self,整個(gè)消息發(fā)送的過程就會(huì)被重啟,發(fā)送的對(duì)象就會(huì)變成返回的對(duì)象。否則,就會(huì)繼續(xù)Normal Forwarding。
    這里叫Fast,是為了區(qū)別下一步的轉(zhuǎn)發(fā)機(jī)制,因?yàn)檫@一步不會(huì)創(chuàng)建任何新的對(duì)象,但是下一步Normal Forwarding會(huì)創(chuàng)建一個(gè)NSInvocation對(duì)象,所以這一步相對(duì)快。
  3. Normal Forwarding
    這一步是runtime最后一次給開發(fā)者的挽救機(jī)會(huì)。
    首先,它會(huì)發(fā)送-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector方法簽名:Objective-C對(duì)方法的參數(shù)個(gè)數(shù)、參數(shù)類型以及返回值類型的描述)
    消息獲得函數(shù)的參數(shù)個(gè)數(shù)、參數(shù)類型和返回值類型。如果- methodSignatureForSelector:返回nil,runtime則會(huì)發(fā)出- (void)doesNotRecognizeSelector:(SEL)aSelector消息,程序這時(shí)也掛掉了。如果返回了一個(gè)函數(shù)簽名,runtime就會(huì)創(chuàng)建一個(gè)NSInvocation對(duì)象并發(fā)送- forwardInvocation:消息給目標(biāo)對(duì)象。

假設(shè)有方法簽名為"@@:@",第一個(gè)@表示返回值類型為id,第二個(gè)@表示的是函數(shù)的調(diào)用者類型,第三個(gè)表示SEL,第四個(gè)@表示需要一個(gè)id類型的參數(shù)。
例子:
- (void)hello;對(duì)應(yīng)的方法簽名:v16@0:8,v16表示返回值類型為空,@0表示receiver(調(diào)用者)類型為id,:8表示SEL 方法標(biāo)識(shí)。
- (id)hello:(id)x對(duì)應(yīng)的方法簽名:@24@0:8@16,@24表示返回值類型為id類型,@0表示receiver(調(diào)用者)類型為id,:8表示SEL方法標(biāo)識(shí),@16標(biāo)識(shí)參數(shù)返回值類型為id類型。
-(void)hello:(id)x :(id)e對(duì)應(yīng)的方法簽名:v32@0:8@16@24,v32表示返回值類型為空,@0表示receiver(調(diào)用者)類型為id,:8表示SEL方法標(biāo)識(shí),@16和@24表示返回值參數(shù)為id類型。
總結(jié):
v16,v32等表示返回類型為空,@16,@24,@32等表示返回值類型或參數(shù)類型為id類型,@0表示receiver類型為id,:8表示SEL方法標(biāo)識(shí)。

最后編輯于
?著作權(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ù)。

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

  • 最近對(duì)經(jīng)濟(jì)學(xué)產(chǎn)生了興趣,便開始日學(xué)之。 每每讀到“萬惡”的商人利用各種“伎倆”欺騙我們消費(fèi)者的感情,便后背冒汗,兩...
    凌易水閱讀 721評(píng)論 0 3
  • 看目錄,…海明威…加西亞馬爾克斯!米蘭昆德拉!…村上春樹(其實(shí)我沒看過他的書,很奇怪吧,我自己都覺得訝異)…就只知...
    I迎迎I閱讀 252評(píng)論 0 0
  • 說到中國,相比起國外經(jīng)常被提起的更加悠閑輕松的教育方式,國內(nèi)刻苦的學(xué)習(xí)方法確實(shí)培養(yǎng)出了不少小有名氣的聰明學(xué)生...
    Andy老師閱讀 352評(píng)論 0 0
  • 今天跟人聊天中 每聊一個(gè)都沒有成功 有時(shí)候覺得自己挺失敗的 我確實(shí)是很不會(huì)跟人聊天 索性今天知道自己在哪里聊失敗了...
    汐時(shí)閱讀 125評(píng)論 0 0

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