2.Runtime儲(chǔ)備知識(shí)
2.1.前言
? ? ? ?在體驗(yàn)中,我寫過(guò)這樣一句話“并且我認(rèn)為這個(gè)技術(shù)算是高階開(kāi)發(fā)里面一個(gè)投機(jī)的技巧,絕大多數(shù)的UI開(kāi)發(fā)都不會(huì)使用runtime,容易出現(xiàn)很嚴(yán)重的問(wèn)題,并且官方也不是特別推薦使用”。
? ? ? ? 但是經(jīng)過(guò)了第一張?bào)w驗(yàn),我會(huì)告訴你。雖然說(shuō)第一在UI開(kāi)發(fā)中你可能不會(huì)經(jīng)常使用,第二使用不當(dāng)時(shí),會(huì)出現(xiàn)很嚴(yán)重的問(wèn)題。第三官方也不是推薦使用的。但是??!即使你并不使用,學(xué)習(xí)Objective-C的運(yùn)行時(shí)Runtime系統(tǒng)也是非常有必要的,并且當(dāng)你學(xué)會(huì)了Runtime系統(tǒng),你會(huì)發(fā)現(xiàn)你可以在任何地方使用到它。畢竟Runtime系統(tǒng)被稱之為iOS開(kāi)發(fā)的黑魔法。
? ? ? ?Objective-C提供了編譯運(yùn)行時(shí),只要有可能,它都可以動(dòng)態(tài)地運(yùn)作。這意味著不僅需要編譯器,還需要運(yùn)行時(shí)系統(tǒng)執(zhí)行編譯的代碼。運(yùn)行時(shí)系統(tǒng)充當(dāng)Objective-C語(yǔ)言的操作系統(tǒng),有了它才能運(yùn)作。
? ? ? ?運(yùn)行時(shí)系統(tǒng)所提供功能是非常強(qiáng)大的,在一些第三方庫(kù)開(kāi)發(fā)中是經(jīng)常使用到的。比如,蘋果不允許我們給Category追加擴(kuò)展屬性,是因?yàn)樗粫?huì)自動(dòng)生成成員變量,那么我們通過(guò)運(yùn)行時(shí)就可以很好的解決這個(gè)問(wèn)題。另外,常見(jiàn)的模型轉(zhuǎn)字典或者字典轉(zhuǎn)模型,對(duì)象歸檔等。后續(xù)我們?cè)賮?lái)學(xué)習(xí)如何應(yīng)用,本節(jié)只是講講理論。
2.2.與Runtime交互
? ? ? ?在使用runtime中,我們會(huì)通過(guò)如下三種方法來(lái)使用runtime。并且使用的難度級(jí)風(fēng)險(xiǎn)也越來(lái)越大。
通過(guò)Objective-C源代碼
通過(guò)Foundation庫(kù)中定義的NSObject提供的方法
通過(guò)直接調(diào)用runtime方法
安全系數(shù)及難度:零
? ? ? ?在我們?nèi)粘i_(kāi)發(fā)中,所有編寫的Objective-C代碼系統(tǒng)都會(huì)自動(dòng)幫助我們編譯成runtime代碼。我們使用它只是寫源代碼并編譯源代碼。當(dāng)編譯包含Objective-C類和方法的代碼時(shí),編譯器會(huì)創(chuàng)建實(shí)現(xiàn)了語(yǔ)言動(dòng)態(tài)特性的數(shù)據(jù)結(jié)構(gòu)和函數(shù)調(diào)用。
安全系數(shù)及難度:低級(jí)
? ? ? ? 在Cocoa編程中,大部分的類都繼承于NSObject,也就是說(shuō)NSObject通常是根類,大部分的類都繼承于NSObject。有些特殊的情況下,NSObject只是提供了它應(yīng)該要做什么的模板,卻沒(méi)有提供所有必須的代碼。
? ? ? ?有些NSObject提供的方法僅僅是為了查詢運(yùn)動(dòng)時(shí)系統(tǒng)的相關(guān)信息,這此方法都可以反查自己。比如-isKindOfClass:和-isMemberOfClass:都是用于查詢?cè)诶^承體系中的位置。-respondsToSelector:指明是否接受特定的消息。+conformsToProtocol:指明是否要求實(shí)現(xiàn)在指定的協(xié)議中聲明的方法。-methodForSelector:提供方法實(shí)現(xiàn)的地址。
2.2.3.通過(guò)直接調(diào)用runtime函數(shù)
安全系數(shù)及難度:高級(jí)
runtime庫(kù)函數(shù)在usr/include/objc目錄下,我們主要關(guān)注是這兩個(gè)頭文件:
#import <objc/runtime.h>
#import <objc/objc.h>
2.3.消息(Message)
? ? ? ?為什么叫消息呢?因?yàn)槊嫦驅(qū)ο缶幊讨?,?duì)象調(diào)用方法叫做發(fā)送消息。在編譯時(shí),應(yīng)用的源代碼就會(huì)被編將對(duì)象發(fā)送消息轉(zhuǎn)換成runtime的objc_msgSend函數(shù)調(diào)用。
? ? ? ?在Objective-C,消息在運(yùn)行時(shí)并不要求實(shí)現(xiàn)。編譯器會(huì)轉(zhuǎn)換消息表達(dá)式:
[receiver message];
? ? ? ?在編譯時(shí)會(huì)轉(zhuǎn)換成類似這樣的函數(shù)調(diào)用:
id objc_msgSend(id self,SEL op,...)
? ? ? ? 具體會(huì)轉(zhuǎn)換成哪個(gè),我們來(lái)看看官方的文章注釋:
/**
* Sends a message with a simple return value to an instance of a class.
* @param self A pointer to the instance of the class that is to receive the message.
* @param op The selector of the method that handles the message.
* @param ...
*?? A variable argument list containing the arguments to the method.
* @return The return value of the method.
* @note When it encounters a method call, the compiler generates a call to one of the
*??functions \c objc_msgSend, \c objc_msgSend_stret, \c objc_msgSendSuper, or \c objc_msgSendSuper_stret.
*??Messages sent to an object’s superclass (using the \c super keyword) are sent using \c objc_msgSendSuper;
*??other messages are sent using \c objc_msgSend. Methods that have data structures as return values
*??are sent using \c objc_msgSendSuper_stret and \c objc_msgSend_stret.
*/
? ? ? ? 也就是說(shuō),我們是通過(guò)編譯器來(lái)自動(dòng)轉(zhuǎn)換成運(yùn)行時(shí)代碼時(shí),它會(huì)根據(jù)類型自動(dòng)轉(zhuǎn)換成下面的其它一個(gè)函數(shù):
objc_msgSend:其它普通的消息都會(huì)通過(guò)該函數(shù)來(lái)發(fā)送
objc_msgSend_stret:消息中需要有數(shù)據(jù)結(jié)構(gòu)作為返回值時(shí),會(huì)通過(guò)該函數(shù)來(lái)發(fā)送消息并接收返回值
objc_msgSendSuper:與objc_msgSend函數(shù)類似,只是它把消息發(fā)送給父類實(shí)例
objc_msgSendSuper_stret:與objc_msgSend_stret函數(shù)類似,只是它把消息發(fā)送給父類實(shí)例并接收數(shù)組結(jié)構(gòu)作為返回值
? ? ? ?另外,如果函數(shù)返回值是浮點(diǎn)類型,官方說(shuō)明如下:
? ? ? ?注意:有另外一個(gè)需要注意的地方是當(dāng)函數(shù)返回的值是浮點(diǎn)類型是,官方注釋有這樣的解釋:
/* Floating-point-returning Messaging Primitives
*
* Use these functions to call methods that return floating-point values
* on the stack.
* Consult your local function call ABI documentation for details.
*
* arm:????objc_msgSend_fpret not used
* i386:?? objc_msgSend_fpret used for `float`, `double`, `long double`.
* x86-64: objc_msgSend_fpret used for `long double`.
*
* arm:????objc_msgSend_fp2ret not used
* i386:?? objc_msgSend_fp2ret not used
* x86-64: objc_msgSend_fp2ret used for `_Complex long double`.
*
* These functions must be cast to an appropriate function pointer type
* before being called.
*/
? ? ? ?其實(shí)總來(lái)來(lái)說(shuō)不用擔(dān)心這個(gè)問(wèn)題,只需要調(diào)用objc_msgSend_fpret函數(shù)就好了。
? ? ? ? 注意事項(xiàng):一定要調(diào)用所調(diào)用的API支持哪些平臺(tái),亂調(diào)在導(dǎo)致部分平臺(tái)上不支持而崩潰的。
? ? ? ?現(xiàn)在我們來(lái)看看當(dāng)消息被發(fā)送到實(shí)例對(duì)象時(shí),它是如何處理的:

? ? ? ? 這個(gè)算是iOS開(kāi)發(fā)消息傳遞的基礎(chǔ)知識(shí)了,就不在更多解釋。一直找到NSObject如果都還沒(méi)有找到,就會(huì)崩潰報(bào)錯(cuò)Unreconized selector。
2.4.Message Forwarding
? ? ? ?當(dāng)發(fā)送消息給一個(gè)不處理該消息的對(duì)象是錯(cuò)誤的。然后在宣布錯(cuò)誤之前,運(yùn)行時(shí)系統(tǒng)給了接收消息的對(duì)象處理消息的第二個(gè)機(jī)會(huì)。
? ? ? ?當(dāng)某對(duì)象不處理某消息時(shí),可以通過(guò)重寫-forwardInvocation:方法來(lái)提供一個(gè)默認(rèn)的消息響應(yīng)或者避免出錯(cuò)。當(dāng)對(duì)象中找不到方法實(shí)現(xiàn)時(shí),會(huì)按照類繼承關(guān)系一層層往上找。我們看看類繼承關(guān)系圖:

? ? ? ?所有元類中的isa指針都指向根元類,而根元類的isa指針則指向自身。根元類是繼承于根類的,與根類的結(jié)構(gòu)體成員一致,都是objc_class結(jié)構(gòu)體,不同的是根元類的isa指針指向自身,而根類的isa指針為nil
我們?cè)倏纯聪⑻幚砹鞒蹋?/p>

? ? ? ? 當(dāng)對(duì)象查詢不到相關(guān)的方法,消息得不到該對(duì)象處理,會(huì)啟動(dòng)“消息轉(zhuǎn)發(fā)”機(jī)制。消息轉(zhuǎn)發(fā)還分為幾個(gè)階段:先詢問(wèn)receiver或者說(shuō)是它所屬的類是否能動(dòng)態(tài)添加方法,以處理當(dāng)前這個(gè)消息,這叫做“動(dòng)態(tài)方法解析”,runtime會(huì)通過(guò)+resolveInstanceMethod:判斷能否處理。如果runtime完成動(dòng)態(tài)添加方法的詢問(wèn)之后,receiver仍然無(wú)法正常響應(yīng)則Runtime會(huì)繼續(xù)向receiver詢問(wèn)是否有其它對(duì)象即其它receiver能處理這條消息,若返回能夠處理的對(duì)象,Runtime會(huì)把消息轉(zhuǎn)給返回的對(duì)象,消息轉(zhuǎn)發(fā)流程也就結(jié)束。若無(wú)對(duì)象返回,Runtime會(huì)把消息有關(guān)的全部細(xì)節(jié)都封裝到NSInvocation對(duì)象中,再給receiver最后一次機(jī)會(huì),令其設(shè)法解決當(dāng)前還未處理的這條消息。
? ? ? ? 我們可以這樣理解:
? ? ? ? 向一個(gè)對(duì)象發(fā)送它不處理的消息是一個(gè)會(huì)報(bào)錯(cuò),但在報(bào)錯(cuò)之前 Runtime系統(tǒng)會(huì)給接收對(duì)象來(lái)處理這些錯(cuò)誤的機(jī)會(huì)。這個(gè)需要用到以下方法:
-(void) forwardInvocation: (NSInvocation*)invocation
? ? ? ? 如果對(duì)象沒(méi)有實(shí)現(xiàn)這個(gè)方法,就調(diào)用NSObject 的forwardInvocation:方法。那句不能識(shí)別消息的錯(cuò)誤,實(shí)際就是NSObject 的forwardInvocation 拋出來(lái)的異常。
? ? ? ? 也就是說(shuō),Message Forwarding的作用就是你可以覆蓋forwardInvocation方法,來(lái)改變NSObject 的拋異常的處理方式。所以,你可以把A不能處理的消息轉(zhuǎn)發(fā)給B去處理。
提示:消息處理越往后,開(kāi)銷也就會(huì)越大,因此最好直接在第一步就可以得到消息處理。
? ? ? ? 我們來(lái)看看NSInvocation官方頭文件:
@interfaceNSInvocation:
NSObject{
@private
__strongvoid*_frame;
__strongvoid*_retdata;
id_signature;
id_container;
uint8_t_retainedArgs;
uint8_t_reserved[15];
}
+(NSInvocation*)invocationWithMethodSignature:(NSMethodSignature*)sig;
@property(readonly,retain)NSMethodSignature*methodSignature;
-(void)retainArguments;
@property(readonly)BOOLargumentsRetained;
@property(nullable,assign)idtarget;
@propertySELselector;
-(void)getReturnValue:(void*)retLoc;
-(void)setReturnValue:(void*)retLoc;
-(void)getArgument:(void*)argumentLocationatIndex:(NSInteger)idx;
-(void)setArgument:(void*)argumentLocationatIndex:(NSInteger)idx;
-(void)invoke;
-(void)invokeWithTarget:(id)target;
@end
? ? ? ? 實(shí)際上NSInvocation是一個(gè)包含了target、selector ,Arguments,也就是它包含了向一個(gè)對(duì)象發(fā)送消息的所有元素:對(duì)象、方法名、參數(shù)序列??梢哉{(diào)用NSInvocation 的invoke 方法將這個(gè)消息激活。
? ? ? ? 后面Message Forwarding會(huì)單獨(dú)繼續(xù)精講。這里做一個(gè)理解。
2.5.類與對(duì)象基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)
2.5.1.Class
? ? ? ? Objective-C類是由Class類型來(lái)表示的,它實(shí)際上是一個(gè)指向objc_class結(jié)構(gòu)體的指針。
? ? ? ? 查看objc/objc.h中Class的定義如下:
/// An opaque type that represents an Objective-C class.
typedefstructobjc_class*Class;
? ? ? ? ?查看objc/runtime.h中objc_class結(jié)構(gòu)體的定義如下:
structobjc_class{
ClassisaOBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Classsuper_classOBJC2_UNAVAILABLE;//
父類
constchar*nameOBJC2_UNAVAILABLE;//
類名
longversionOBJC2_UNAVAILABLE;//
類的版本信息,默認(rèn)為0
longinfoOBJC2_UNAVAILABLE;//
類信息,供運(yùn)行期使用的一些位標(biāo)識(shí)
longinstance_sizeOBJC2_UNAVAILABLE;//
該類的實(shí)例變量大小
structobjc_ivar_list*ivarsOBJC2_UNAVAILABLE;//
該類的成員變量鏈表
structobjc_method_list**methodListsOBJC2_UNAVAILABLE;//
方法定義的鏈表
structobjc_cache*cacheOBJC2_UNAVAILABLE;//
方法緩存
structobjc_protocol_list*protocolsOBJC2_UNAVAILABLE;//
協(xié)議鏈表
#endif
}OBJC2_UNAVAILABLE;
? ? ? ? ?我們可以看到每個(gè)類結(jié)構(gòu)體都會(huì)有一個(gè)isa指針,它是指向元類的。它還有一個(gè)父類指針super_class,指針父類。還包含了類的名稱name、類的版本信息version、類的一些標(biāo)識(shí)信息info、實(shí)例大小instance_size、成員變量地址列表ivars、方法地址列表methodLists、緩存最近使用的方法地址cache、協(xié)議列表protocols。其中有一下幾個(gè)字段是我們需要特別關(guān)注的:
isa:需要注意的是在Objective-C中,所有的類自身也是一個(gè)對(duì)象,這個(gè)對(duì)象的Class里面也有一個(gè)isa指針,它指向metaClass(元類),我們會(huì)在后面介紹它。
super_class:指向該類的父類,如果該類已經(jīng)是最頂層的根類(如NSObject或NSProxy),則super_class為NULL。
cache:用于緩存最近使用的方法。一個(gè)接收者對(duì)象接收到一個(gè)消息時(shí),它會(huì)根據(jù)isa指針去查找能夠響應(yīng)這個(gè)消息的對(duì)象。在實(shí)際使用中,這個(gè)對(duì)象只有一部分方法是常用的,很多方法其實(shí)很少用或者根本用不上。這種情況下,如果每次消息來(lái)時(shí),我們都是methodLists中遍歷一遍,性能勢(shì)必很差。這時(shí),cache就派上用場(chǎng)了。在我們每次調(diào)用過(guò)一個(gè)方法后,這個(gè)方法就會(huì)被緩存到cache列表中,下次調(diào)用的時(shí)候runtime就會(huì)優(yōu)先去cache中查找,如果cache沒(méi)有,才去methodLists中查找方法。這樣,對(duì)于那些經(jīng)常用到的方法的調(diào)用,但提高了調(diào)用的效率。
version:我們可以使用這個(gè)字段來(lái)提供類的版本信息。這對(duì)于對(duì)象的序列化非常有用,它可是讓我們識(shí)別出不同類定義版本中實(shí)例變量布局的改變。
? ? ? ? ?針對(duì)cache,我們用下面例子來(lái)說(shuō)明其執(zhí)行過(guò)程:
NSArray*array=[[NSArrayalloc]init];
其流程是:
? ? ? ? [NSArray alloc]先被執(zhí)行。因?yàn)镹SArray沒(méi)有+alloc方法,于是去父類NSObject去查找。
? ? ? ? 檢測(cè)NSObject是否響應(yīng)+alloc方法,發(fā)現(xiàn)響應(yīng),于是檢測(cè)NSArray類,并根據(jù)其所需的內(nèi)存空間大小開(kāi)始分配內(nèi)存空間,然后把isa指針指向NSArray類。同時(shí),+alloc也被加進(jìn)cache列表里面。
? ? ? ? 接著,執(zhí)行-init方法,如果NSArray響應(yīng)該方法,則直接將其加入cache;如果不響應(yīng),則去父類查找。
? ? ? ? ?在后期的操作中,如果再以[[NSArray alloc] init]這種方式來(lái)創(chuàng)建數(shù)組,則會(huì)直接從cache中取出相應(yīng)的方法,直接調(diào)用。
2.5.1.1.objc_object與id
? ? ? ? ?objc_object是表示一個(gè)類的實(shí)例的結(jié)構(gòu)體,它的定義如下(objc/objc.h):
#if !OBJC_TYPES_DEFINED
/// An opaque type that represents an Objective-C class.
typedefstructobjc_class*Class;
/// Represents an instance of a class.
structobjc_object{
ClassisaOBJC_ISA_AVAILABILITY;
};
/// A pointer to an instance of a class.
typedefstructobjc_object*id;
#endif
? ? ? ?從官方的頭文件可以看到objc_object是一個(gè)結(jié)構(gòu)體并且只有一個(gè)成員,即指向其類的isa指針。這樣,當(dāng)我們向一個(gè)Objective-C對(duì)象發(fā)送消息時(shí),runtime系統(tǒng)會(huì)根據(jù)實(shí)例對(duì)象的isa指針找到這個(gè)實(shí)例對(duì)象所屬的類。Runtime系統(tǒng)會(huì)在類的方法列表及父類的方法列表中去尋找與消息對(duì)應(yīng)的selector指向的方法。找到后即運(yùn)行這個(gè)方法。
? ? ? ?當(dāng)創(chuàng)建一個(gè)特定類的實(shí)例對(duì)象時(shí),分配的內(nèi)存包含一個(gè)objc_object數(shù)據(jù)結(jié)構(gòu),然后是類的實(shí)例變量的數(shù)據(jù)。NSObject類的alloc和allocWithZone:方法使用函數(shù)class_createInstance來(lái)創(chuàng)建objc_object數(shù)據(jù)結(jié)構(gòu)。
? ? ? ?另外還有我們常見(jiàn)的id,它是一個(gè)objc_object結(jié)構(gòu)類型的指針。它的存在可以讓我們實(shí)現(xiàn)類似于C++中泛型的一些操作。該類型的對(duì)象可以轉(zhuǎn)換為任何一種對(duì)象,有點(diǎn)類似于C語(yǔ)言中void *指針類型的作用。
2.5.1.2.objc_cache
? ? ? ? 上面提到了objc_class結(jié)構(gòu)體中的cache字段,它用于緩存調(diào)用過(guò)的方法。這個(gè)字段是一個(gè)指向objc_cache結(jié)構(gòu)體的指針,其定義如下:
typedefstructobjc_cache*CacheOBJC2_UNAVAILABLE;
#define CACHE_BUCKET_NAME(B)??((B)->method_name)
#define CACHE_BUCKET_IMP(B)?? ((B)->method_imp)
#define CACHE_BUCKET_VALID(B) (B)
#ifndef __LP64__
#define CACHE_HASH(sel, mask) (((uintptr_t)(sel)>>2) & (mask))
#else
#define CACHE_HASH(sel, mask) (((unsigned int)((uintptr_t)(sel)>>3)) & (mask))
#endif
structobjc_cache{
unsignedintmask/* total = mask + 1 */OBJC2_UNAVAILABLE;
unsignedintoccupiedOBJC2_UNAVAILABLE;
Methodbuckets[1]OBJC2_UNAVAILABLE;
};
該結(jié)構(gòu)體的字段描述如下:
mask:一個(gè)整數(shù),指定分配的緩存bucket的總數(shù)。在方法查找過(guò)程中,Objective-C runtime使用這個(gè)字段來(lái)確定開(kāi)始線性查找數(shù)組的索引位置。指向方法selector的指針與該字段做一個(gè)AND位操作(index = (mask & selector))。這可以作為一個(gè)簡(jiǎn)單的hash散列算法。
occupied:一個(gè)整數(shù),指定實(shí)際占用的緩存bucket的總數(shù)。
buckets:指向Method數(shù)據(jù)結(jié)構(gòu)指針的數(shù)組。這個(gè)數(shù)組可能包含不超過(guò)mask+1個(gè)元素。需要注意的是,指針可能是NULL,表示這個(gè)緩存bucket沒(méi)有被占用,另外被占用的bucket可能是不連續(xù)的。這個(gè)數(shù)組可能會(huì)隨著時(shí)間而增長(zhǎng)。
2.5.2.元類(Meta Class)
? ? ? ? 在上面我們提到,所有的類自身也是一個(gè)對(duì)象,我們可以向這個(gè)對(duì)象發(fā)送消息(即調(diào)用類方法)。如:
NSArray*array=[NSArrayarray];
? ? ? ? 這個(gè)例子中,+array消息發(fā)送給了NSArray類,而這個(gè)NSArray也是一個(gè)對(duì)象。既然是對(duì)象,那么它也是一個(gè)objc_object指針,它包含一個(gè)指向其類的一個(gè)isa指針。那么這些就有一個(gè)問(wèn)題了,這個(gè)isa指針指向什么呢?為了調(diào)用+array方法,這個(gè)類的isa指針必須指向一個(gè)包含這些類方法的一個(gè)objc_class結(jié)構(gòu)體。這就引出了meta-class的概念
meta-class是一個(gè)類對(duì)象的類。
? ? ? ? 當(dāng)我們向一個(gè)對(duì)象發(fā)送消息時(shí),runtime會(huì)在這個(gè)對(duì)象所屬的這個(gè)類的方法列表中查找方法;而向一個(gè)類發(fā)送消息時(shí),會(huì)在這個(gè)類的meta-class的方法列表中查找。
? ? ? ? ?meta-class之所以重要,是因?yàn)樗鎯?chǔ)著一個(gè)類的所有類方法。每個(gè)類都會(huì)有一個(gè)單獨(dú)的meta-class,因?yàn)槊總€(gè)類的類方法基本不可能完全相同。
? ? ? ? 其實(shí),meta-class也是一個(gè)類,也可以向它發(fā)送一個(gè)消息,那么它的isa又是指向什么呢?為了不讓這種結(jié)構(gòu)無(wú)限延伸下去,Objective-C的設(shè)計(jì)者讓所有的meta-class的isa指向基類的meta-class,以此作為它們的所屬類。即,任何NSObject繼承體系下的meta-class都使用NSObject的meta-class作為自己的所屬類,而基類的meta-class的isa指針是指向它自己。這樣就形成了一個(gè)完美的閉環(huán)。
? ? ? ? 通過(guò)上面的描述,再加上對(duì)objc_class結(jié)構(gòu)體中super_class指針的分析,我們就可以描繪出類及相應(yīng)meta-class類的一個(gè)繼承體系了,如下圖所示:

? ? ? ? 對(duì)于NSObject繼承體系來(lái)說(shuō),其實(shí)例方法對(duì)體系中的所有實(shí)例、類和meta-class都是有效的;而類方法對(duì)于體系內(nèi)的所有類和meta-class都是有效的。
3.操作函數(shù)
3.1.類名(name)
/**
* Returns the name of a class.
*
* @param cls A class object.
*
* @return The name of the class, or the empty string if \e cls is \c Nil.
*/
OBJC_EXPORTconstchar*class_getName(Classcls)
__OSX_AVAILABLE_STARTING(__MAC_10_5,__IPHONE_2_0);
? ? ? ? 對(duì)于class_getName函數(shù),如果傳入的cls為Nil,則返回一個(gè)char字字符串。
3.2.父類(super_class)和元類(meta-class)
/**
* Returns a Boolean value that indicates whether a class object is a metaclass.
*
* @param cls A class object.
*
* @return \c YES if \e cls is a metaclass, \c NO if \e cls is a non-meta class,
*??\c NO if \e cls is \c Nil.
*/
OBJC_EXPORTBOOLclass_isMetaClass(Classcls)
__OSX_AVAILABLE_STARTING(__MAC_10_5,__IPHONE_2_0);
/**
* Returns the superclass of a class.
*
* @param cls A class object.
*
* @return The superclass of the class, or \c Nil if
*??\e cls is a root class, or \c Nil if \e cls is \c Nil.
*
* @note You should usually use \c NSObject's \c superclass method instead of this function.
*/
OBJC_EXPORTClassclass_getSuperclass(Classcls)
__OSX_AVAILABLE_STARTING(__MAC_10_5,__IPHONE_2_0);
/**
* Sets the superclass of a given class.
*
* @param cls The class whose superclass you want to set.
* @param newSuper The new superclass for cls.
*
* @return The old superclass for cls.
*
* @warning You should not use this function.
*/
OBJC_EXPORTClassclass_setSuperclass(Classcls,ClassnewSuper)
__OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_5,__MAC_10_5,__IPHONE_2_0,__IPHONE_2_0);
class_isMetaClass函數(shù),如果是cls是元類,則返回YES;如果否或者傳入的cls為Nil,則返回NO。
class_getSuperclass函數(shù),當(dāng)cls為Nil或者cls為根類時(shí),返回Nil。不過(guò)通常我們可以使用NSObject類的superclass方法來(lái)達(dá)到同樣的目的。
class_setSuperclass函數(shù),為一個(gè)cls設(shè)置一個(gè)新的superclass,并且返回cls以前的superclass。
3.3.版本(version)
/**
* Returns the version number of a class definition.
*
* @param cls A pointer to a \c Class data structure. Pass
*??the class definition for which you wish to obtain the version.
*
* @return An integer indicating the version number of the class definition.
*
* @see class_setVersion
*/
OBJC_EXPORTintclass_getVersion(Classcls)
__OSX_AVAILABLE_STARTING(__MAC_10_0,__IPHONE_2_0);
/**
* Sets the version number of a class definition.
*
* @param cls A pointer to an Class data structure.
*??Pass the class definition for which you wish to set the version.
* @param version An integer. Pass the new version number of the class definition.
*
* @note You can use the version number of the class definition to provide versioning of the
*??interface that your class represents to other classes. This is especially useful for object
*??serialization (that is, archiving of the object in a flattened form), where it is important to
*??recognize changes to the layout of the instance variables in different class-definition versions.
* @note Classes derived from the Foundation framework \c NSObject class can set the class-definition
*??version number using the \c setVersion: class method, which is implemented using the \c class_setVersion function.
*/
OBJC_EXPORTvoidclass_setVersion(Classcls,intversion)
__OSX_AVAILABLE_STARTING(__MAC_10_0,__IPHONE_2_0);
class_getVersion函數(shù)可以獲取類定義的版本號(hào),返回一個(gè)int類型。
class_setVersion函數(shù)對(duì)cls設(shè)置一個(gè)int類型的版本號(hào),不過(guò)通常我們可以使用NSObject類的setVersion方法來(lái)達(dá)到同樣的目的。
3.4.實(shí)例變量大小(instance_size)
/**
* Returns the size of instances of a class.
*
* @param cls A class object.
*
* @return The size in bytes of instances of the class \e cls, or \c 0 if \e cls is \c Nil.
*/
OBJC_EXPORTsize_tclass_getInstanceSize(Classcls)
__OSX_AVAILABLE_STARTING(__MAC_10_5,__IPHONE_2_0);
3.5.成員變量(ivars)及屬性
/**
* Returns the \c Ivar for a specified instance variable of a given class.
*
* @param cls The class whose instance variable you wish to obtain.
* @param name The name of the instance variable definition to obtain.
*
* @return A pointer to an \c Ivar data structure containing information about
*??the instance variable specified by \e name.
*/
OBJC_EXPORTIvarclass_getInstanceVariable(Classcls,constchar*name)
__OSX_AVAILABLE_STARTING(__MAC_10_0,__IPHONE_2_0);
/**
* Returns the Ivar for a specified class variable of a given class.
*
* @param cls The class definition whose class variable you wish to obtain.
* @param name The name of the class variable definition to obtain.
*
* @return A pointer to an \c Ivar data structure containing information about the class variable specified by \e name.
*/
OBJC_EXPORTIvarclass_getClassVariable(Classcls,constchar*name)
__OSX_AVAILABLE_STARTING(__MAC_10_5,__IPHONE_2_0);
/**
* Describes the instance variables declared by a class.
*
* @param cls The class to inspect.
* @param outCount On return, contains the length of the returned array.
*??If outCount is NULL, the length is not returned.
*
* @return An array of pointers of type Ivar describing the instance variables declared by the class.
*??Any instance variables declared by superclasses are not included. The array contains *outCount
*??pointers followed by a NULL terminator. You must free the array with free().
*
*??If the class declares no instance variables, or cls is Nil, NULL is returned and *outCount is 0.
*/
OBJC_EXPORTIvar*class_copyIvarList(Classcls,unsignedint*outCount)
__OSX_AVAILABLE_STARTING(__MAC_10_5,__IPHONE_2_0);
/**
* Adds a new instance variable to a class.
*
* @return YES if the instance variable was added successfully, otherwise NO
*???????? (for example, the class already contains an instance variable with that name).
*
* @note This function may only be called after objc_allocateClassPair and before objc_registerClassPair.
*?????? Adding an instance variable to an existing class is not supported.
* @note The class must not be a metaclass. Adding an instance variable to a metaclass is not supported.
* @note The instance variable's minimum alignment in bytes is 1<
*?????? variable depends on the ivar's type and the machine architecture.
*?????? For variables of any pointer type, pass log2(sizeof(pointer_type)).
*/
OBJC_EXPORTBOOLclass_addIvar(Classcls,constchar*name,size_tsize,
uint8_talignment,constchar*types)
__OSX_AVAILABLE_STARTING(__MAC_10_5,__IPHONE_2_0);
class_getInstanceVariable函數(shù),它返回一個(gè)指向包含name指定的成員變量信息的objc_ivar結(jié)構(gòu)體的指針(Ivar)。
class_getClassVariable函數(shù),目前沒(méi)有找到關(guān)于Objective-C中類變量的信息,一般認(rèn)為Objective-C不支持類變量。注意,返回的列表不包含父類的成員變量和屬性。
class_copyIvarList函數(shù),它返回一個(gè)指向成員變量信息的數(shù)組,數(shù)組中每個(gè)元素是指向該成員變量信息的objc_ivar結(jié)構(gòu)體的指針。這個(gè)數(shù)組不包含在父類中聲明的變量。outCount指針?lè)祷財(cái)?shù)組的大小。需要注意的是,我們必須使用free()來(lái)釋放這個(gè)數(shù)組。
class_addIvar函數(shù),我們知道Objective-C不支持往已存在的類中添加實(shí)例變量,因此不管是系統(tǒng)庫(kù)提供的提供的類,還是我們自定義的類,都無(wú)法動(dòng)態(tài)添加成員變量。但如果我們通過(guò)運(yùn)行時(shí)來(lái)創(chuàng)建一個(gè)類的話,又應(yīng)該如何給它添加成員變量呢?這時(shí)我們就可以使用class_addIvar函數(shù)了。不過(guò)需要注意的是,這個(gè)方法只能在objc_allocateClassPair函數(shù)與objc_registerClassPair之間調(diào)用。另外,這個(gè)類也不能是元類。成員變量的按字節(jié)最小對(duì)齊量是1<