iOS理論基礎(chǔ)(二)

1.@property 的本質(zhì)是什么?ivar、getter、setter 是如何生成并添加到這個(gè)類(lèi)中的

@property 的本質(zhì)是什么?

@property = ivar + getter + setter;

下面解釋下:

“屬性” (property)有兩大概念:ivar(實(shí)例變量)、存取方法(access method = getter + setter)。

“屬性” (property)作為 Objective-C 的一項(xiàng)特性,主要的作用就在于封裝對(duì)象中的數(shù)據(jù)。

ivar、getter、setter 是如何生成并添加到這個(gè)類(lèi)中的?

“自動(dòng)合成”( autosynthesis)

完成屬性定義后,編譯器會(huì)自動(dòng)編寫(xiě)訪問(wèn)這些屬性所需的方法,此過(guò)程叫做“自動(dòng)合成”(autosynthesis)。需要強(qiáng)調(diào)的是,這個(gè)過(guò)程由編譯 器在編譯期執(zhí)行,所以編輯器里看不到這些“合成方法”(synthesized method)的源代碼。

2. @protocol 和 category 中如何使用 @property

在 protocol 中使用 property 只會(huì)生成 setter 和 getter 方法聲明,我們使用屬性的目的,是希望遵守我協(xié)議的對(duì)象能實(shí)現(xiàn)該屬性

category 使用 @property 也是只會(huì)生成 setter 和 getter 方法的聲明,如果我們真的需要給 category 增加屬性的實(shí)現(xiàn),需要借助于運(yùn)行時(shí)的兩個(gè)函數(shù):

objc_setAssociatedObject

objc_getAssociatedObject

3.runtime 如何實(shí)現(xiàn) weak 屬性

要實(shí)現(xiàn) weak 屬性,首先要搞清楚 weak 屬性的特點(diǎn):

weak 此特質(zhì)表明該屬性定義了一種“非擁有關(guān)系” (nonowning relationship)。為這種屬性設(shè)置新值時(shí),設(shè)置方法既不保留新值,也不釋放舊值。此特質(zhì)同 assign 類(lèi)似, 然而在屬性所指的對(duì)象遭到摧毀時(shí),屬性值也會(huì)清空(nil out)。

那么 runtime 如何實(shí)現(xiàn) weak 變量的自動(dòng)置nil?

runtime 對(duì)注冊(cè)的類(lèi), 會(huì)進(jìn)行布局,對(duì)于 weak 對(duì)象會(huì)放入一個(gè) hash 表中。 用 weak 指向的對(duì)象內(nèi)存地址作為 key,當(dāng)此對(duì)象的引用計(jì)數(shù)為0的時(shí)候會(huì) dealloc,假如 weak 指向的對(duì)象內(nèi)存地址是a,那么就會(huì)以a為鍵, 在這個(gè) weak 表中搜索,找到所有以a為鍵的 weak 對(duì)象,從而設(shè)置為 nil。

4. @property中有哪些屬性關(guān)鍵字?/ @property 后面可以有哪些修飾符?

屬性可以擁有的特質(zhì)分為四類(lèi):

原子性---nonatomic特質(zhì)

在默認(rèn)情況下,由編譯器合成的方法會(huì)通過(guò)鎖定機(jī)制確保其原子性(atomicity)。如果屬性具備 nonatomic 特質(zhì),則不使用自旋鎖。請(qǐng)注意,盡管沒(méi)有名為“atomic”的特質(zhì)(如果某屬性不具備 nonatomic 特質(zhì),那它就是“原子的” ( atomic) ),但是仍然可以在屬性特質(zhì)中寫(xiě)明這一點(diǎn),編譯器不會(huì)報(bào)錯(cuò)。若是自己定義存取方法,那么就應(yīng)該遵從與屬性特質(zhì)相符的原子性。

讀/寫(xiě)權(quán)限---readwrite(讀寫(xiě))、readonly (只讀)

內(nèi)存管理語(yǔ)義---assign、strong、weak、unsafe_unretained、copy

方法名---getter=、setter=

getter=的樣式:

@property (nonatomic, getter=isOn)BOOLon;

( `setter=`這種不常用,也不推薦使用。故不在這里給出寫(xiě)法。)

setter=一般用在特殊的情境下,比如:

在數(shù)據(jù)反序列化、轉(zhuǎn)模型的過(guò)程中,服務(wù)器返回的字段如果以init開(kāi)頭,所以你需要定義一個(gè)init開(kāi)頭的屬性,但默認(rèn)生成的setter與getter方法也會(huì)以init開(kāi)頭,而編譯器會(huì)把所有以init開(kāi)頭的方法當(dāng)成初始化方法,而初始化方法只能返回 self 類(lèi)型,因此編譯器會(huì)報(bào)錯(cuò)。

這時(shí)你就可以使用下面的方式來(lái)避免編譯器報(bào)錯(cuò):

@property(nonatomic, strong, getter=p_initBy, setter=setP_initBy:)NSString*initBy;

另外也可以用關(guān)鍵字進(jìn)行特殊說(shuō)明,來(lái)避免編譯器報(bào)錯(cuò):

@property(nonatomic, readwrite, copy, null_resettable)NSString*initBy;- (NSString*)initBy__attribute__((objc_method_family(none)));

不常用的:nonnull,null_resettable,nullable

5. weak屬性需要在dealloc中置nil么?

不需要。

在ARC環(huán)境無(wú)論是強(qiáng)指針還是弱指針都無(wú)需在 dealloc 設(shè)置為 nil , ARC 會(huì)自動(dòng)幫我們處理

即便是編譯器不幫我們做這些,weak也不需要在 dealloc 中置nil:

6.@synthesize和@dynamic分別有什么作用?

a.@property有兩個(gè)對(duì)應(yīng)的詞,一個(gè)是 @synthesize,一個(gè)是 @dynamic。如果 @synthesize和 @dynamic都沒(méi)寫(xiě),那么默認(rèn)的就是@syntheszie var = _var;

b.@synthesize 的語(yǔ)義是如果你沒(méi)有手動(dòng)實(shí)現(xiàn) setter 方法和 getter 方法,那么編譯器會(huì)自動(dòng)為你加上這兩個(gè)方法。

c.@dynamic 告訴編譯器:屬性的 setter 與 getter 方法由用戶自己實(shí)現(xiàn),不自動(dòng)生成。(當(dāng)然對(duì)于 readonly 的屬性只需提供 getter 即可)。假如一個(gè)屬性被聲明為 @dynamic var,然后你沒(méi)有提供@setter方法和 @getter 方法,編譯的時(shí)候沒(méi)問(wèn)題,但是當(dāng)程序運(yùn)行到instance.var = someVar,由于缺 setter 方法會(huì)導(dǎo)致程序崩潰;或者當(dāng)運(yùn)行到someVar = var時(shí),由于缺 getter 方法同樣會(huì)導(dǎo)致崩潰。編譯時(shí)沒(méi)問(wèn)題,運(yùn)行時(shí)才執(zhí)行相應(yīng)的方法,這就是所謂的動(dòng)態(tài)綁定。

7. ARC下,不顯式指定任何屬性關(guān)鍵字時(shí),默認(rèn)的關(guān)鍵字都有哪些?

a.對(duì)應(yīng)基本數(shù)據(jù)類(lèi)型默認(rèn)關(guān)鍵字是atomic,readwrite,assign?

b. 對(duì)于普通的 Objective-C 對(duì)象atomic,readwrite,strong

8. 用@property聲明的NSString(或NSArray,NSDictionary)經(jīng)常使用copy關(guān)鍵字,為什么?如果改用strong關(guān)鍵字,可能造成什么問(wèn)題?

a.因?yàn)楦割?lèi)指針可以指向子類(lèi)對(duì)象,使用 copy 的目的是為了讓本對(duì)象的屬性不受外界影響,使用 copy 無(wú)論給我傳入是一個(gè)可變對(duì)象還是不可對(duì)象,我本身持有的就是一個(gè)不可變的副本.

b.如果我們使用是 strong ,那么這個(gè)屬性就有可能指向一個(gè)可變對(duì)象,如果這個(gè)可變對(duì)象在外部被修改了,那么會(huì)影響該屬性.

aa.在非集合類(lèi)對(duì)象中:對(duì) immutable 對(duì)象進(jìn)行 copy 操作,是指針復(fù)制,mutableCopy 操作時(shí)內(nèi)容復(fù)制;對(duì) mutable 對(duì)象進(jìn)行 copy 和 mutableCopy 都是內(nèi)容復(fù)制.

bb.在集合類(lèi)對(duì)象中,對(duì) immutable 對(duì)象進(jìn)行 copy,是指針復(fù)制, mutableCopy 是內(nèi)容復(fù)制;對(duì) mutable 對(duì)象進(jìn)行 copy 和 mutableCopy 都是內(nèi)容復(fù)制。但是:集合對(duì)象的內(nèi)容復(fù)制僅限于對(duì)象本身,對(duì)象元素仍然是指針復(fù)制。

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

實(shí)例變量 = 成員變量 = ivar

如果使用了屬性的話,那么編譯器就會(huì)自動(dòng)編寫(xiě)訪問(wèn)屬性所需的方法,此過(guò)程叫做“自動(dòng)合成”( auto synthesis)。需要強(qiáng)調(diào)的是,這個(gè)過(guò)程由編譯器在編譯期執(zhí)行,所以編輯器里看不到這些“合成方法” (synthesized method)的源代碼。除了生成方法代碼之外,編譯器還要自動(dòng)向類(lèi)中添加適當(dāng)類(lèi)型的實(shí)例變量,并且在屬性名前面加下劃線,以此作為實(shí)例變量的名字。

@interfaceCYLPerson:NSObject@propertyNSString*firstName;@propertyNSString*lastName;@end

在上例中,會(huì)生成兩個(gè)實(shí)例變量,其名稱(chēng)分別為_(kāi)firstName與_lastName。也可以在類(lèi)的實(shí)現(xiàn)代碼里通過(guò)@synthesize語(yǔ)法來(lái)指定實(shí)例變量的名字:

@implementationCYLPerson@synthesizefirstName = _myFirstName;@synthesizelastName = _myLastName;@end

上述語(yǔ)法會(huì)將生成的實(shí)例變量命名為_(kāi)myFirstName與_myLastName,而不再使用默認(rèn)的名字。一般情況下無(wú)須修改默認(rèn)的實(shí)例變量名,但是如果你不喜歡以下劃線來(lái)命名實(shí)例變量,那么可以用這個(gè)辦法將其改為自己想要的名字。筆者還是推薦使用默認(rèn)的命名方案,因?yàn)槿绻腥硕紙?jiān)持這套方案,那么寫(xiě)出來(lái)的代碼大家都能看得懂。

總結(jié)下 @synthesize 合成實(shí)例變量的規(guī)則,有以下幾點(diǎn):

如果指定了成員變量的名稱(chēng),會(huì)生成一個(gè)指定的名稱(chēng)的成員變量,

如果這個(gè)成員已經(jīng)存在了就不再生成了.

如果是@synthesize foo;還會(huì)生成一個(gè)名稱(chēng)為foo的成員變量,也就是說(shuō):

如果沒(méi)有指定成員變量的名稱(chēng)會(huì)自動(dòng)生成一個(gè)屬性同名的成員變量,

如果是@synthesize foo = _foo;就不會(huì)生成成員變量了.

假如 property 名為 foo,存在一個(gè)名為_(kāi)foo的實(shí)例變量,那么還會(huì)自動(dòng)合成新變量么? 不會(huì)。如下圖:

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

回答這個(gè)問(wèn)題前,我們要搞清楚一個(gè)問(wèn)題,什么情況下不會(huì)autosynthesis(自動(dòng)合成)?

同時(shí)重寫(xiě)了 setter 和 getter 時(shí)

重寫(xiě)了只讀屬性的 getter 時(shí)

使用了 @dynamic 時(shí)

在 @protocol 中定義的所有屬性

在 category 中定義的所有屬性

重載的屬性

當(dāng)你在子類(lèi)中重載了父類(lèi)中的屬性,你必須 使用@synthesize來(lái)手動(dòng)合成ivar。

除了后三條,對(duì)其他幾個(gè)我們可以總結(jié)出一個(gè)規(guī)律:當(dāng)你想手動(dòng)管理 @property 的所有內(nèi)容時(shí),你就會(huì)嘗試通過(guò)實(shí)現(xiàn) @property 的所有“存取方法”(the accessor methods)或者使用@dynamic來(lái)達(dá)到這個(gè)目的,這時(shí)編譯器就會(huì)認(rèn)為你打算手動(dòng)管理 @property,于是編譯器就禁用了 autosynthesis(自動(dòng)合成)。

因?yàn)橛辛?autosynthesis(自動(dòng)合成),大部分開(kāi)發(fā)者已經(jīng)習(xí)慣不去手動(dòng)定義ivar,而是依賴(lài)于 autosynthesis(自動(dòng)合成),但是一旦你需要使用ivar,而 autosynthesis(自動(dòng)合成)又失效了,如果不去手動(dòng)定義ivar,那么你就得借助@synthesize來(lái)手動(dòng)合成 ivar。

其實(shí),@synthesize語(yǔ)法還有一個(gè)應(yīng)用場(chǎng)景,但是不太建議大家使用:

可以在類(lèi)的實(shí)現(xiàn)代碼里通過(guò)@synthesize語(yǔ)法來(lái)指定實(shí)例變量的名字:

@implementationCYLPerson@synthesizefirstName = _myFirstName;@synthesizelastName = _myLastName;@end

上述語(yǔ)法會(huì)將生成的實(shí)例變量命名為_(kāi)myFirstName與_myLastName,而不再使用默認(rèn)的名字。一般情況下無(wú)須修改默認(rèn)的實(shí)例變量名,但是如果你不喜歡以下劃線來(lái)命名實(shí)例變量,那么可以用這個(gè)辦法將其改為自己想要的名字。筆者還是推薦使用默認(rèn)的命名方案,因?yàn)槿绻腥硕紙?jiān)持這套方案,那么寫(xiě)出來(lái)的代碼大家都能看得懂。

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

在 Objective-C 中向 nil 發(fā)送消息是完全有效的——只是在運(yùn)行時(shí)不會(huì)有任何作用:

如果一個(gè)方法返回值是一個(gè)對(duì)象,那么發(fā)送給nil的消息將返回0(nil)。例如:

Person * motherInlaw = [[aPersonspouse]mother];

如果 spouse 對(duì)象為 nil,那么發(fā)送給 nil 的消息 mother 也將返回 nil。 2. 如果方法返回值為指針類(lèi)型,其指針大小為小于或者等于sizeof(void*),float,double,long double 或者 long long 的整型標(biāo)量,發(fā)送給 nil 的消息將返回0。 2. 如果方法返回值為結(jié)構(gòu)體,發(fā)送給 nil 的消息將返回0。結(jié)構(gòu)體中各個(gè)字段的值將都是0。 2. 如果方法的返回值不是上述提到的幾種情況,那么發(fā)送給 nil 的消息的返回值將是未定義的。

具體原因如下:

objc是動(dòng)態(tài)語(yǔ)言,每個(gè)方法在運(yùn)行時(shí)會(huì)被動(dòng)態(tài)轉(zhuǎn)為消息發(fā)送,即:objc_msgSend(receiver, selector)。

objc在向一個(gè)對(duì)象發(fā)送消息時(shí),runtime庫(kù)會(huì)根據(jù)對(duì)象的isa指針找到該對(duì)象實(shí)際所屬的類(lèi),然后在該類(lèi)中的方法列表以及其父類(lèi)方法列表中尋找方法運(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ò)誤。

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

[obj foo]該方法編譯之后就是objc_msgSend()函數(shù)調(diào)用.
[obj foo];在objc編譯時(shí),會(huì)被轉(zhuǎn)意為:objc_msgSend(obj, @selector(foo));。

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

簡(jiǎn)單來(lái)說(shuō):

當(dāng)調(diào)用該對(duì)象上某個(gè)方法,而該對(duì)象上沒(méi)有實(shí)現(xiàn)這個(gè)方法的時(shí)候, 可以通過(guò)“消息轉(zhuǎn)發(fā)”進(jìn)行解決。

簡(jiǎn)單的流程如下,在上一題中也提到過(guò):

objc是動(dòng)態(tài)語(yǔ)言,每個(gè)方法在運(yùn)行時(shí)會(huì)被動(dòng)態(tài)轉(zhuǎn)為消息發(fā)送,即:objc_msgSend(receiver, selector)。

objc在向一個(gè)對(duì)象發(fā)送消息時(shí),runtime庫(kù)會(huì)根據(jù)對(duì)象的isa指針找到該對(duì)象實(shí)際所屬的類(lèi),然后在該類(lèi)中的方法列表以及其父類(lèi)方法列表中尋找方法運(yùn)行,如果,在最頂層的父類(lèi)中依然找不到相應(yīng)的方法時(shí),程序在運(yùn)行時(shí)會(huì)掛掉并拋出異常unrecognized selector sent to XXX 。但是在這之前,objc的運(yùn)行時(shí)會(huì)給出三次拯救程序崩潰的機(jī)會(huì):

?Method resolution

objc運(yùn)行時(shí)會(huì)調(diào)用+resolveInstanceMethod:或者+resolveClassMethod:,讓你有機(jī)會(huì)提供一個(gè)函數(shù)實(shí)現(xiàn)。如果你添加了函數(shù),那運(yùn)行時(shí)系統(tǒng)就會(huì)重新啟動(dòng)一次消息發(fā)送的過(guò)程,否則 ,運(yùn)行時(shí)就會(huì)移到下一步,消息轉(zhuǎn)發(fā)(Message Forwarding)。

Fast forwarding

如果目標(biāo)對(duì)象實(shí)現(xiàn)了-forwardingTargetForSelector:,Runtime 這時(shí)就會(huì)調(diào)用這個(gè)方法,給你把這個(gè)消息轉(zhuǎn)發(fā)給其他對(duì)象的機(jī)會(huì)。 只要這個(gè)方法返回的不是nil和self,整個(gè)消息發(fā)送的過(guò)程就會(huì)被重啟,當(dāng)然發(fā)送的對(duì)象會(huì)變成你返回的那個(gè)對(duì)象。否則,就會(huì)繼續(xù)Normal Fowarding。 這里叫Fast,只是為了區(qū)別下一步的轉(zhuǎn)發(fā)機(jī)制。因?yàn)檫@一步不會(huì)創(chuàng)建任何新的對(duì)象,但下一步轉(zhuǎn)發(fā)會(huì)創(chuàng)建一個(gè)NSInvocation對(duì)象,所以相對(duì)更快點(diǎn)。 3. Normal forwarding

這一步是Runtime最后一次給你挽救的機(jī)會(huì)。首先它會(huì)發(fā)送-methodSignatureForSelector:消息獲得函數(shù)的參數(shù)和返回值類(lèi)型。如果-methodSignatureForSelector:返回nil,Runtime則會(huì)發(fā)出-doesNotRecognizeSelector:消息,程序這時(shí)也就掛掉了。如果返回了一個(gè)函數(shù)簽名,Runtime就會(huì)創(chuàng)建一個(gè)NSInvocation對(duì)象并發(fā)送-forwardInvocation:消息給目標(biāo)對(duì)象。

14.一個(gè)objc對(duì)象如何進(jìn)行內(nèi)存布局?(考慮有父類(lèi)的情況)

所有父類(lèi)的成員變量和自己的成員變量都會(huì)存放在該對(duì)象所對(duì)應(yīng)的存儲(chǔ)空間中.

每一個(gè)對(duì)象內(nèi)部都有一個(gè)isa指針,指向他的類(lèi)對(duì)象,類(lèi)對(duì)象中存放著本對(duì)象的

對(duì)象方法列表(對(duì)象能夠接收的消息列表,保存在它所對(duì)應(yīng)的類(lèi)對(duì)象中)

成員變量的列表,

屬性列表,

它內(nèi)部也有一個(gè)isa指針指向元對(duì)象(meta class),元對(duì)象內(nèi)部存放的是類(lèi)方法列表,類(lèi)對(duì)象內(nèi)部還有一個(gè)superclass的指針,指向他的父類(lèi)對(duì)象。

每個(gè) Objective-C 對(duì)象都有相同的結(jié)構(gòu),如下圖所示:

翻譯過(guò)來(lái)就是

Objective-C 對(duì)象的結(jié)構(gòu)圖

ISA指針

根類(lèi)的實(shí)例變量

倒數(shù)第二層父類(lèi)的實(shí)例變量

...

父類(lèi)的實(shí)例變量

類(lèi)的實(shí)例變量

根對(duì)象就是NSObject,它的superclass指針指向nil

類(lèi)對(duì)象既然稱(chēng)為對(duì)象,那它也是一個(gè)實(shí)例。類(lèi)對(duì)象中也有一個(gè)isa指針指向它的元類(lèi)(meta class),即類(lèi)對(duì)象是元類(lèi)的實(shí)例。元類(lèi)內(nèi)部存放的是類(lèi)方法列表,根元類(lèi)的isa指針指向自己,superclass指針指向NSObject類(lèi)。

15.一個(gè)objc對(duì)象的isa的指針指向什么?有什么作用?

指向他的類(lèi)對(duì)象,從而可以找到對(duì)象上的方法

16.下面的代碼輸出什么?

@implementationSon:Father

- (id)init? {? ? ??

?self = [superinit]

;if(self) {NSLog(@"%@",NSStringFromClass([selfclass]));NSLog(@"%@",NSStringFromClass([superclass]));? ? ? }returnself;? }@end

答案:

都輸出 Son

NSStringFromClass([self class]) = Son

NSStringFromClass([super class]) = Son

這個(gè)題目主要是考察關(guān)于 Objective-C 中對(duì) self 和 super 的理解。

我們都知道:self 是類(lèi)的隱藏參數(shù),指向當(dāng)前調(diào)用方法的這個(gè)類(lèi)的實(shí)例。那 super 呢?

很多人會(huì)想當(dāng)然的認(rèn)為“ super 和 self 類(lèi)似,應(yīng)該是指向父類(lèi)的指針吧!”。這是很普遍的一個(gè)誤區(qū)。其實(shí) super 是一個(gè) Magic Keyword, 它本質(zhì)是一個(gè)編譯器標(biāo)示符,和 self 是指向的同一個(gè)消息接受者!他們兩個(gè)的不同點(diǎn)在于:super 會(huì)告訴編譯器,調(diào)用 class 這個(gè)方法時(shí),要去父類(lèi)的方法,而不是本類(lèi)里的。

上面的例子不管調(diào)用[self class]還是[super class],接受消息的對(duì)象都是當(dāng)前Son *xxx這個(gè)對(duì)象。

當(dāng)使用 self 調(diào)用方法時(shí),會(huì)從當(dāng)前類(lèi)的方法列表中開(kāi)始找,如果沒(méi)有,就從父類(lèi)中再找;而當(dāng)使用 super 時(shí),則從父類(lèi)的方法列表中開(kāi)始找。然后調(diào)用父類(lèi)的這個(gè)方法。

最后編輯于
?著作權(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),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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