原文地址:http://www.apple.com.cn/developer/Documentation/index.html
Objective-C 2.0運行時系統編程指南
概述
本部分包括如下內容:
本文檔的組織結構
參考
Objective-C語言將決定盡可能的從編譯和鏈接時推遲到運行時。只要有可能,Objective-C總是使用動態
的方式來解決問題。這意味著Objective-C語言不僅需要一個編譯器,同時也需要一個運行時系統來執行
編譯好的代碼。這兒的運行時系統扮演的角色類似于Objective-C語言的操作系統,Objective-C基于該系
統來工作。
本文檔將具體介紹NSObject類以及Objective-C程序是如何和運行時系統交互的。特別地,本文檔還給
出來怎樣在運行時動態地加載新類和將消息轉發給其它對象的范例,同時也給出了怎樣在程序運行時獲取
對象信息的方法。
通常,如果是僅僅寫一個Cocoa程序,您不需要知道和理解Objective-C運行時系統的底層細節,但這篇
文檔仍然值得推薦您閱讀一下,以了解Objective-C運行時系統的原理,并能更好的利用Objective-C的優
點。
本文檔的組織結構
本文檔包括如下章節:
?“運行時系統的版本和平臺”?和運行時系統的交互”
?“消息”
?“動態方法解析”
?“消息轉發”?“類型編碼”?“屬性聲明”
參考
Objective-C 2.0運行時系統參考庫描述了Objective-C運行庫的數據結構和函數接口。您的程序可以通過這
些接口來和Objective-C運行時系統交互。例如,您可以增加一個類或者方法,或者獲得所有類的定義列表
等。
Objective-C 2.0程序設計語言介紹了Objective-C語言本身。
Objective-C版本說明給出了在最近版本的Mac OS X系統中關于Objecitive-C運行時系統的一些改動。
運行時系統的版本和平臺
在不同的平臺上Objective-C運行時系統的版本也不相同。
整理:http://www.aisidechina.com/forum/
原文地址:http://www.apple.com.cn/developer/Documentation/index.html
本部分包含如下內容:
早期版本和現行版本
平臺
早期版本和現行版本
Objective-C運行時系統有兩個已知版本:早期版本和現行版本。現行版本主要是Objective-C 2.0及與其相
關的新特性。早期版本的編程接口見Objective-C 1運行時系統參考庫;現行版本的編程接口見Objective-C
2.0運行時系統參考庫。
在現行版本中,最顯著的新特性就是實例變量是"健壯“(non-fragile)的:
?在早期版本中,如果您改變類中實例變量的布局,您必須重新編譯該類的所有子類。?在現行版本中,如果您改變類中實例變量的布局,您無需重新編譯該類的任何子類。
此外,現行版本支持聲明property的synthesis屬性(參考Objective-C 2.0程序設計語言的屬性一節)。平臺
iPhone程序和Mac OS X v10.5及以后的系統中的64位程序使用的都是Objective-C運行時系統的現行版
本。
其它情況(Mac OS X系統中的32位程序)使用的是早期版本。和運行時系統的交互
Objective-C程序有三種途徑和運行時系統交互:通過Objective-C源代碼;通過Foundation框架中類NSObject的方法;通過直接調用運行時系統的函數。
本部分包含如下內容:
通過Objective-C源代碼
通過類NSObject的方法
通過運行時系統的函數
通過Objective-C源代碼大部分情況下,運行時系統在后臺自動運行,您只需編寫和編譯Objective-C源代碼。
當您編譯Objective-C類和方法時,編譯器為實現語言動態特性將自動創建一些數據結構和函數。這些數據
結構包含類定義和協議類定義中的信息,如在Objective-C 2.0程序設計語言中定義類和協議類一節所討論
的類的對象和協議類的對象,方法選標,實例變量模板,以及其它來自于源代碼的信息。運行時系統的主
要功能就是根據源代碼中的表達式發送消息,如"消息”一節所述。
通過類NSObject的方法
Cocoa程序中絕大部分類都是NSObject類的子類,所以大部分都繼承了NSObject類的方法,因而繼承
了NSObject的行為。(NSProxy類是個例外;更多細節參考““消息轉發”一節。)然而,某些情況下,NSObject類僅僅定義了完成某件事情的模板,而沒有提供所有需要的代碼。
整理:http://www.aisidechina.com/forum/
原文地址:http://www.apple.com.cn/developer/Documentation/index.html
例如,NSObject類定義了description方法,返回該類內容的字符串表示。這主要是用來調試程序——GDB中的print-object方法就是直接打印出該方法返回的字符串。NSObject類中該方法的
實現并不知道子類中的內容,所以它只是返回類的名字和對象的地址。NSObject的子類可以重新實現
該方法以提供更多的信息。例如,NSArray類改寫了該方法來返回NSArray類包含的每個對象的內容。
某些NSObject的方法只是簡單地從運行時系統中獲得信息,從而允許對象進行一定程度的自我檢查。
例如,class返回對象的類;isKindOfClass:和isMemberOfClass:則檢查對象是否在指定的
類繼承體系中;respondsToSelector:檢查對象能否響應指定的消息;conformsToProtocol:檢查對象是否實現了指定協議類的方法;methodForSelector:則返回指定方法實現的地址。
通過運行時系統的函數
運行時系統是一個有公開接口的動態庫,由一些數據結構和函數的集合組成,這些數據結構和函數的聲明
頭文件在/usr/include/objc中。這些函數支持用純C的函數來實現和Objective-C同樣的功能。還
有一些函數構成了NSObject類方法的基礎。這些函數使得訪問運行時系統接口和提供開發工具成為可
能。盡管大部分情況下它們在Objective-C程序不是必須的,但是有時候對于Objecitve-C程序來說某些函
數是非常有用的。 這些函數的文檔參見Objective-C 2.0運行時系統參考庫。
消息
本章描述了代碼的消息表達式如何轉換為對objc_msgSend函數的調用,如何通過名字來指定一個方
法,以及如何使用objc_msgSend函數。
本部分包含如下內容:
獲得方法地址objc_msgSend函數
使用隱藏的參數
獲得方法地址
避免動態綁定的唯一辦法就是取得方法的地址,并且直接象函數調用一樣調用它。當一個方法會被連續調
用很多次,而且您希望節省每次調用方法都要發送消息的開銷時,使用方法地址來調用方法就顯得很有效。
利用NSObject類中的methodForSelector:方法,您可以獲得一個指向方法實現的指針,并可以
使用該指針直接調用方法實現。methodForSelector:返回的指針和賦值的變量類型必須完全一致,
包括方法的參數類型和返回值類型都在類型識別的考慮范圍中。
下面的例子展示了怎么使用指針來調用setFilled:的方法實現:
void (*setter)(id, SEL, BOOL);
int i;
setter = (void (*)(id, SEL, BOOL))[target
methodForSelector:@selector(setFilled:)];
整理:http://www.aisidechina.com/forum/
原文地址:http://www.apple.com.cn/developer/Documentation/index.html
for ( i = 0; i < 1000, i++ )
setter(targetList[i], @selector(setFilled:), YES);
方法指針的第一個參數是接收消息的對象(self),第二個參數是方法選標(_cmd)。這兩個參數在方
法中是隱藏參數,但使用函數的形式來調用方法時必須顯示的給出。
使用methodForSelector:來避免動態綁定將減少大部分消息的開銷,但是這只有在指定的消息被重
復發送很多次時才有意義,例如上面的for循環。
注意,methodForSelector:是Cocoa運行時系統的提供的功能,而不是Objective-C語言本身的功
能。
objc_msgSend函數
在Objective-C中,消息是直到運行的時候才和方法實現綁定的。編譯器會把一個消息表達式,
轉換成一個對消息函數objc_msgSend的調用。該函數有兩個主要參數:消息接收者和消息對應的方法
名字——也就是方法選標:
同時接收消息中的任意數目的參數:
該消息函數做了動態綁定所需要的一切:
?它首先找到選標所對應的方法實現。因為不同的類對同一方法可能會有不同的實現,所以找到的
方法實現依賴于消息接收者的類型。
?然后將消息接收者對象(指向消息接收者對象的指針)以及方法中指定的參數傳給找到的方法實
現。
?最后,將方法實現的返回值作為該函數的返回值返回。注意:編譯器將自動插入調用該消息函數的代碼。您無須在代碼中顯示調用該消息函數。
消息機制的關鍵在于編譯器為類和對象生成的結構。每個類的結構中至少包括兩個基本元素:
?指向父類的指針。
?類的方法表。方法表將方法選標和該類的方法實現的地址關聯起來。例如,setOrigin::的方
法選標和setOrigin::的方法實現的地址關聯,display的方法選標和display的
方法實現的地址關聯,等等。
當新的對象被創建時,其內存同時被分配,實例變量也同時被初始化。對象的第一個實例變量是一個指向
該對象的類結構的指針,叫做isa。通過該指針,對象可以訪問它對應的類以及相應的父類。
注意:盡管嚴格來說這并不是Obective-C語言的一部分,但是在Objective-C運行時系統中對象需要有isa指針。對象和結構體struct objc_object(在objc/objc.h中定義)必須“一致”。然而,
您很少需要創建您自己的根對象,因為從NSObject或者NSProxy繼承的對象都自動包括isa變量。
[receiver message]
objc_msgSend(receiver, selector)
objc_msgSend(receiver, selector, arg1, arg2, ...)
整理:http://www.aisidechina.com/forum/
原文地址:http://www.apple.com.cn/developer/Documentation/index.html
類和對象的結構如圖3-1所示。
圖3-1消息框架
當對象收到消息時,消息函數首先根據該對象的isa指針找到該對象所對應的類的方法表,并從表中尋找
該消息對應的方法選標。如果找不到,objc_msgSend將繼續從父類中尋找,直到NSObject類。一
旦找到了方法選標,objc_msgSend則以消息接收者對象為參數調用,調用該選標對應的方法實現。
這就是在運行時系統中選擇方法實現的方式。在面向對象編程中,一般稱作方法和消息動態綁定的過程。
為了加快消息的處理過程,運行時系統通常會將使用過的方法選標和方法實現的地址放入緩存中。每個類
都有一個獨立的緩存,同時包括繼承的方法和在該類中定義的方法。消息函數會首先檢查消息接收者對象
對應的類的緩存(理論上,如果一個方法被使用過一次,那么它很可能被再次使用)。如果在緩存中已經
有了需要的方法選標,則消息僅僅比函數調用慢一點點。如果程序運行了足夠長的時間,幾乎每個消息都
能在緩存中找到方法實現。程序運行時,緩存也將隨著新的消息的增加而增加。
使用隱藏的參數
整理:http://www.aisidechina.com/forum/
原文地址:http://www.apple.com.cn/developer/Documentation/index.html
當objc_msgSend找到方法對應的實現時,它將直接調用該方法實現,并將消息中所有的參數都傳遞給
方法實現,同時,它還將傳遞兩個隱藏的參數:
?接收消息的對象
?方法選標
這些參數幫助方法實現獲得了消息表達式的信息。它們被認為是”隱藏“的是因為它們并沒有在定義方法的
源代碼中聲明,而是在代碼編譯時是插入方法的實現中的。
盡管這些參數沒有被顯示聲明,但在源代碼中仍然可以引用它們(就象可以引用消息接收者對象的實例變
量一樣)。在方法中可以通過self來引用消息接收者對象,通過選標_cmd來引用方法本身。在下面的
例子中,_cmd指的是strange方法,self指的收到strange消息的對象。
- strange
{
id target = getTheReceiver();
SEL method = getTheMethod();
if ( target == self || method == _cmd )
return nil;
return [target performSelector:method];
}
在這兩個參數中,self更有用一些。實際上,它是在方法實現中訪問消息接收者對象的實例變量的途徑。
動態方法解析
本章將描述怎樣動態地提供一個方法的實現。
本部分包含如下內容:
動態方法解析
動態加載
動態方法解析
有時候,您需要動態地提供一個方法的實現。例如,Objective-C中屬性(Property)(參考Objective-C 2.0程序設計語言中屬性小節)前的修飾符@dynamic
表示編譯器須動態地生成該屬性對應地方法。
您可以通過實現resolveInstanceMethod:和resolveClassMethod:來動態地實現給定選標
的對象方法或者類方法。
@dynamic propertyName;
整理:http://www.aisidechina.com/forum/
原文地址:http://www.apple.com.cn/developer/Documentation/index.html
Objective-C方法可以認為是至少有兩個參數——self和_cmd——的C函數。您可以通過class_addMethod方法將一個函數加入到類的方法中。例如,有如下的函數:
您可以通過resolveInstanceMethod:將它作為類方法resolveThisMethodDynamically的實現:
void dynamicMethodIMP(id self, SEL _cmd) {
// implementation ....
}
@implementation MyClass
+ (BOOL)resolveInstanceMethod:(SEL)aSEL
{
if (aSEL == @selector(resolveThisMethodDynamically)) {
class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");
return YES;
}
return [super resolveInstanceMethod:aSEL];
}
@end
通常消息轉發(見“消息轉發”)和動態方法解析是互不相干的。在進入消息轉發機制之前,respondsToSelector:和instancesRespondToSelector:會被首先調用。您可以在這兩個
方法中為傳進來的選標提供一個IMP。如果您實現了resolveInstanceMethod:方法但是仍然希望正
常的消息轉發機制進行,您只需要返回NO就可以了。
動態加載
Objective-C程序可以在運行時鏈接和載入新的類和范疇類。新載入的類和在程序啟動時載入的類并沒有區
別。
動態加載可以用在很多地方。例如,系統配置中的模塊就是被動態加載的。
在Cocoa環境中,動態加載一般被用來對應用程序進行定制。您的程序可以在運行時加載其他程序員編寫
的模塊——和Interface Build載入定制的調色板以及系統配置程序載入定制的模塊的類似。 這些模塊通過
您許可的方式擴展了您的程序,而您無需自己來定義或者實現。您提供了框架,而其它的程序員提供了實
現。
盡管已經有一個運行時系統的函數來動態加載Mach-O文件中的Objective-C模塊
(objc_loadModules,在objc/objc-load.h中定義),Cocoa的NSBundle類為動態加載
提供了一個更方便的接口——一個面向對象的,已和相關服務集成的接口。關于NSBundle類的更多相關
整理:http://www.aisidechina.com/forum/
原文地址:http://www.apple.com.cn/developer/Documentation/index.html
信息請參考Foundation框架中關于NSBundle類的文檔。關于Mach-O文件的有關信息請參考Mac OS X
ABI Mach-O文件格式參考庫。
消息轉發
通常,給一個對象發送它不能處理的消息會得到出錯提示,然而,Objective-C運行時系統在拋出錯誤之前,
會給消息接收對象發送一條特別的消息來通知該對象。
本部分包含如下內容::
消息轉發
消息轉發和多重繼承
消息代理對象
消息轉發和類繼承
消息轉發
如果一個對象收到一條無法處理的消息,運行時系統會在拋出錯誤前,給該對象發送一條
forwardInvocation:消息,該消息的唯一參數是個NSInvocation類型的對象——該對象封裝了
原始的消息和消息的參數。
您可以實現forwardInvocation:方法來對不能處理的消息做一些默認的處理,也可以以其它的某種
方式來避免錯誤被拋出。如forwardInvocation:的名字所示,它通常用來將消息轉發給其它的對象。
關于消息轉發的作用,您可以考慮如下情景:假設,您需要設計一個能夠響應negotiate消息的對象,
并且能夠包括其它類型的對象對消息的響應。 通過在negotiate方法的實現中將negotiate消息
轉發給其它的對象來很容易的達到這一目的。
更進一步,假設您希望您的對象和另外一個類的對象對negotiate的消息的響應完全一致。一種可能的
方式就是讓您的類繼承其它類的方法實現。 然后,有時候這種方式不可行,因為您的類和其它類可能需要
在不同的繼承體系中響應negotiate消息。
雖然您的類無法繼承其它類的negotiate方法,您仍然可以提供一個方法實現,這個方法實現只是簡單
的將negotiate消息轉發給其他類的對象,就好像從其它類那兒“借”來的現一樣。如下所示:
- negotiate
{
if ( [someOtherObject respondsTo:@selector(negotiate)] )
return [someOtherObject negotiate];
return self;
}
這種方式顯得有欠靈活,特別是有很多消息您都希望傳遞給其它對象時,您必須為每一種消息提供方法實
現。此外,這種方式不能處理未知的消息。當您寫下代碼時,所有您需要轉發的消息的集合也必須確定。
然而,實際上,這個集合會隨著運行時事件的發生,新方法或者新類的定義而變化。
整理:http://www.aisidechina.com/forum/
原文地址:http://www.apple.com.cn/developer/Documentation/index.html
forwardInvocation:消息給這個問題提供了一個更特別的,動態的解決方案:當一個對象由于沒有
相應的方法實現而無法響應某消息時,運行時系統將通過forwardInvocation:消息通知該對象。每
個對象都從NSObject類中繼承了forwardInvocation:方法。然而,NSObject中的方法實現
只是簡單地調用了doesNotRecognizeSelector:。通過實現您自己的forwardInvocation:方法,您可以在該方法實現中將消息轉發給其它對象。
要轉發消息給其它對象,forwardInvocation:方法所必須做的有:?決定將消息轉發給誰,并且
?將消息和原來的參數一塊轉發出去
消息可以通過invokeWithTarget:方法來轉發:
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
if ([someOtherObject respondsToSelector:
[anInvocation selector]])
[anInvocation invokeWithTarget:someOtherObject];
else
[super forwardInvocation:anInvocation];
}
轉發消息后的返回值將返回給原來的消息發送者。您可以將返回任何類型的返回值,包括:id,結構體,
浮點數等。
forwardInvocation:方法就像一個不能識別的消息的分發中心,將這些消息轉發給不同接收對象。
或者它也可以象一個運輸站將所有的消息都發送給同一個接收對象。它可以將一個消息翻譯成另外一個消
息,或者簡單的"吃掉“某些消息,因此沒有響應也沒有錯誤。forwardInvocation:方法也可以對不
同的消息提供同樣的響應,這一切都取決于方法的具體實現。該方法所提供是將不同的對象鏈接到消息鏈
的能力。
更多消息轉發的信息,參考Foundation框架參考庫中NSInvocation類的文檔。消息轉發和多重繼承
消息轉發很象繼承,并且可以用來在Objective-C程序中模擬多重繼承。如圖5-1所示, 一個對象通過轉
發來響應消息,看起來就象該對象從別的類那借來了或者”繼承“了方法實現一樣。
圖5-1消息轉發
注意:forwardInvocation:方法只有在消息接收對象中無法正常響應消息時才會被調用。 所以,
如果您希望您的對象將negotiate消息轉發給其它對象,您的對象不能有negotiate方法。否則,forwardInvocation:將不可能會被調用。
整理:http://www.aisidechina.com/forum/
原文地址:http://www.apple.com.cn/developer/Documentation/index.html
在上圖中,Warrior類的一個對象實例將negotiate消息轉發給Diplomat類的一個實例。看起來,Warrior類似乎和Diplomat類一樣, 響應negotiate消息,并且行為和Diplomat一樣(盡管實際上是Diplomat類響應了該消息)。
轉發消息的對象看起來有兩個繼承體系分支——自己的和響應消息的對象的。在上面的例子中,Warrior看起來同時繼承自Diplomat和自己的父類。
消息轉發提供了多重繼承的很多特性。然而,兩者有很大的不同:多重繼承是將不同的行為封裝到單個的
對象中,有可能導致龐大的,復雜的對象。而消息轉發是將問題分解到更小的對象中,但是又以一種對消
息發送對象來說完全透明的方式將這些對象聯系起來。
消息代理對象
消息轉發不僅和繼承很象,它也使得以一個輕量級的對象(消息代理對象)代表更多的對象進行消息處理
成為可能。
Objective-C 2.0程序設計語言中“遠程消息”一節中的代理類就是這樣一個代理對象。代理類負責將消息轉
發給遠程消息接收對象的管理細節,保證消息參數的傳輸等等。但是消息類沒有進一步的復制遠程對象的
功能,它只是將遠程對象映射到一個本地地址上,從而能夠接收其它應用程序的消息。
同時也存在著其它類型的消息代理對象。例如,假設您有個對象需要操作大量的數據——它可能需要創建
一個復雜的圖片或者需要從磁盤上讀一個文件的內容。創建一個這樣的對象是很費時的,您可能希望能推
遲它的創建時間——直到它真正需要時,或者系統資源空閑時。同時,您又希望至少有一個預留的對象和
程序中其它對象交互。
在這種情況下,你可以為該對象創建一個輕量的代理對象。該代理對象可以有一些自己的功能,例如響應
數據查詢消息,但是它主要的功能是代表某個對象,當時間到來時,將消息轉發給被代表的對象。當代理
對象的forwardInvocation:方法收到需要轉發給被代表的對象的消息時,代理對象會保證所代表的
對象已經存在,否則就創建它。所有發到被代表的對象的消息都要經過代理對象,對程序來說,代理對象
和被代表的對象是一樣的。
消息轉發和類繼承
整理:http://www.aisidechina.com/forum/
原文地址:http://www.apple.com.cn/developer/Documentation/index.html
盡管消息轉發很“象”繼承,但它不是繼承。例如在NSObject類中,方法respondsToSelector:和isKindOfClass:只會出現在繼承鏈中,而不是消息轉發鏈中。例如,如果向一個Warrior類的對象
詢問它能否響應negotiate消息,
返回值是NO,盡管該對象能夠接收和響應negotiate。(見圖5-1。)
大部分情況下,
NO是正確的響應。但不是所有時候都是的。例如,如果您使用消息轉發來創建一個代理
對象以擴展某個類的能力,這兒的消息轉發必須和繼承一樣,盡可能的對用戶透明。如果您希望您的代理
對象看起來就象是繼承自它代表的對象一樣,您需要重新實現respondsToSelector:和isKindOfClass:方法:
if ( [aWarrior respondsToSelector:@selector(negotiate)] )
...
- (BOOL)respondsToSelector:(SEL)aSelector
{
if ( [super respondsToSelector:aSelector] )
return YES;
else {
/* Here, test whether the aSelector message can *
* be forwarded to another object and whether that *
* object can respond to it. Return YES if it can. */
}
return NO;
}
除了respondsToSelector:和isKindOfClass:外,instancesRespondToSelector:方法也必須重新實現。如果您使用的是協議類,需要重新實現的還有conformsToProtocol:方法。
類似地,如果對象需要轉發遠程消息,則methodSignatureForSelector:方法必須能夠返回實際
響應消息的方法的描述。例如,如果對象需要將消息轉發給它所代表的對象,您可能需要如下的methodSignatureForSelector:實現:
- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector
{
NSMethodSignature* signature = [super methodSignatureForSelector:selector];
if (!signature) {
signature = [surrogate methodSignatureForSelector:selector];
整理:http://www.aisidechina.com/forum/
原文地址:http://www.apple.com.cn/developer/Documentation/index.html
}
return signature;
}
您也可以將消息轉發的部分放在一段私有的代碼里,然后從forwardInvocation:調用它。
本節中涉及的方法在Foundation框架參考庫中的NSObject類的文檔中都有描述。關于invokeWithTarget:的具體信息,請參考Foundation框架參考庫中NSInvocation類的文檔。
類型編碼
為了和運行時系統協作,編譯器將方法的返回類型和參數類型都編碼成一個字符串,并且和方法選標關聯
在一起。這些編碼在別的上下文環境中同樣有用,所以您可以直接使用@encode()編譯指令來得到具體
的編碼。給定一個類型,@encode()將返回該類型的編碼字符串。類型可以是基本類型例如整形,指針,
結構體或者聯合體,也可以是一個類,就和C語言中的sizeof()操作符的參數一樣,可以是任何類型。
下表列出了這些類型編碼。注意,它們可能很多和您使用的對象編碼有一些重合。然而,這兒列出來的有
些編碼是您寫編碼器時候不會使用的,也有一些不是@encode()產生的,但是在您寫編碼器的時候是會
使用的。(關于對象編碼的更多信息,請參考Foundation框架參考庫中的NSCoder類文檔。)
c
i
s
l
q
C
I
S
注意: 消息轉發是一個比較高級的技術,僅適用于沒有其它更好的解決辦法的情況。它并不是用來代替
繼承的。如果您必須使用該技術,請確定您已經完全理解了轉發消息的類和接收轉發消息的類的行為。
char *buf1 = @encode(int **);
char *buf2 = @encode(struct key);
char *buf3 = @encode(Rectangle);
表6-1Objective-C類型編碼
編碼
含義
char
int
short
long
在64位程序中,l為32位。
long long
unsigned char
unsigned int
unsigned short
整理:http://www.aisidechina.com/forum/
原文地址:http://www.apple.com.cn/developer/Documentation/index.html
L unsigned long
Q
f
d
B
v
*
@
#
:
[array type]
{name=type...}
(name=type...)bnum
^type?
數組的類型編碼以方括號來表示,緊接著左方括號的是數組元素的數量,然后是數據元素的類型。例如,
一個12個浮點數(floats)指針的數組可以表示如下:
結構體和聯合體分別用大括號和小括號表示。括號中首先是結構體標簽,然后是一個=符號,接著是結構體
中各個成員的編碼。例如,結構體
unsigned long long
float
double
C++標準的bool或者C99標準的_Bool
void
字符串(char *)
對象(無論是靜態指定的還是通過id引用的)
類(Class)
方法選標(SEL)
數組
結構體
聯合體
num個bit的位域
type類型的指針
未知類型(其它時候,一般用來指函數指針)
重要:Objective-C不支持long double類型。@encode(long double)和double一樣,返
回的字符串都是d。
[12^f]
typedef struct example {
id anObject;
char *aString;
int anInt;
整理:http://www.aisidechina.com/forum/
} Example;
原文地址:http://www.apple.com.cn/developer/Documentation/index.html
的編碼如下:
定義的類型名(Example)和結構體標簽(example)有同樣的編碼結果。指向結構體類型的指針的編
碼同樣也包含了結構體內部數據成員的編碼信息,如下所示:
然而,更高層次的間接關聯就沒有了內部數據成員的編碼信息:
對象的編碼類似結構體。 例如,@encode()對NSObject編碼如下:
NSObject類僅聲明了一個Class類型的實例變量,isa。
注意,盡管有一些編碼無法從
@encode()的結果中直接得到,但是運行時系統會使用它們來表示協議
類中方法的修飾符,這些編碼如表6-2所示。
r
n
N
o
O
R
V
屬性聲明
當編譯器遇到一個屬性(Property)聲明時(參考Objective-C 2.0程序設計語言中的屬性小節),編譯器
將產生一些描述性的元數據與屬性所在的類或者協議類關聯。您可以通過函數訪問元數據,這些函數支持
在類或者協議類中通過名字來查找,通過@encode獲得屬性的類型編碼,將屬性的特征(Attribute)作為C字符串的數組返回等。每個類或者協議類都維護了一個聲明了的屬性列表。
{example=@*i}
^{example=@*i}
^^{example}
{NSObject=#}
表6-2Objective-C方法編碼
編碼
含義
const
in
inout
out
bycopy
byref
oneway
整理:http://www.aisidechina.com/forum/
原文地址:http://www.apple.com.cn/developer/Documentation/index.html
本部分包含如下內容:
屬性類型和相關函數
屬性類型編碼
屬性特征的描述范例
屬性類型和相關函數
屬性(Property)類型定義了對描述屬性的結構體objc_property的不透明的句柄。
您可以使用函數class_copyPropertyList和protocol_copyPropertyList來獲得類(包
括范疇類)或者協議類中的屬性列表:
例如,有如下的類聲明:
typedef struct objc_property *Property;
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
objc_property_t *protocol_copyPropertyList(Protocol *proto, unsigned int
*outCount)
@interface Lender : NSObject {
float alone;
}
@property float alone;
@end
您可以象這樣獲得它的屬性:
您還可以通過property_getName函數獲得屬性的名字:
函數class_getProperty和protocol_getProperty則在類或者協議類中返回具有給定名字
的屬性的引用:
id LenderClass = objc_getClass("Lender");
unsigned int outCount;
objc_property_t *properties = class_copyPropertyList(LenderClass, &outCount);
const char *property_getName(objc_property_t property)
objc_property_t class_getProperty(Class cls, const char *name)
objc_property_t protocol_getProperty(Protocol *proto, const char *name, BOOL
isRequiredProperty, BOOL isInstanceProperty)
整理:http://www.aisidechina.com/forum/
原文地址:http://www.apple.com.cn/developer/Documentation/index.html
通過property_getAttributes函數可以獲得屬性的名字和@encode編碼。關于類型編碼的更多
細節,參考“類型編碼“一節;關于屬性的類型編碼,見“屬性類型編碼”及“屬性特征的描述范例”。
綜合起來,您可以通過下面的代碼得到一個類中所有的屬性。
const char *property_getAttributes(objc_property_t property)
id LenderClass = objc_getClass("Lender");
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(LenderClass, &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
fprintf(stdout, "%s %s\n", property_getName(property),
property_getAttributes(property));
}
屬性類型編碼
property_getAttributes函數將返回屬性(Property)的名字,@encode 編碼,以及其它特
征(Attribute)。
?property_getAttributes返回的字符串以字母T開始,接著是@encode編碼和逗號。
?如果屬性有readonly修飾,則字符串中含有R和逗號。
?如果屬性有copy或者retain修飾,則字符串分別含有C或者&,然后是逗號。
?如果屬性定義有定制的getter和setter方法,則字符串中有G或者S跟著相應的方法名以及逗號
(例如,GcustomGetter,ScustomSetter:,,)。
如果屬性是只讀的,且有定制的get訪問方法,則描述到此為止。
?字符串以V然后是屬性的名字結束。
范例請參考“屬性特征的描述范例”一節。屬性特征的描述范例
給定如下定義:
下表給出了屬性(Property)聲明以及property_getAttributes返回的相應的字符串:
enum FooManChu { FOO, MAN, CHU };
struct YorkshireTeaStruct { int pot; char lady; };
typedef struct YorkshireTeaStruct YorkshireTeaStructType;
union MoneyUnion { float alone; double down; };
整理:http://www.aisidechina.com/forum/
原文地址:http://www.apple.com.cn/developer/Documentation/index.html
屬性聲明
屬性描述
Tc,VcharDefault
Td,VdoubleDefault
Ti,VenumDefault
Tf,VfloatDefault
Ti,VintDefault
Tl,VlongDefault
Ts,VshortDefault
Ti,VsignedDefault
T{YorkshireTeaStruct="pot"i"lady"c},Vs
tructDefault
T{YorkshireTeaStruct="pot"i"lady"c},Vt
ypedefDefault
T(MoneyUnion="alone"f"down"d),VunionDe
fault
TI,VunsignedDefault
T^?,VfunctionPointerDefault
T@,VidDefault
T^i,VintPointer
@property char charDefault;
@property double
doubleDefault;
@property enum FooManChu
enumDefault;
@property float floatDefault;
@property int intDefault;
@property long longDefault;
@property short shortDefault;
@property
signedDefault;
@property
YorkshireTeaStruct
structDefault;
@property
YorkshireTeaStructType
typedefDefault;
signed
struct
@property union MoneyUnion
unionDefault;
@property unsigned
unsignedDefault;
@property int
(*functionPointerDefault)(ch
ar *);
@property id idDefault;
Note: the compiler warns:no
'assign', 'retain', or 'copy'
attribute is specified -
'assign' is assumed"
@property int *intPointer;
整理:http://www.aisidechina.com/forum/
原文地址:http://www.apple.com.cn/developer/Documentation/index.html
@property void T^v,VvoidPointerDefault
*voidPointerDefault;
@property int intSynthEquals;
In the implementation block:
@synthesize
intSynthEquals=_intSynthEqua
ls;
@property(getter=intGetFoo,
setter=intSetFoo:) int
intSetterGetter;
@property(readonly) int
intReadonly;
@property(getter=isIntReadOn
lyGetter, readonly) int
intReadonlyGetter;
@property(readwrite) int
intReadwrite;
@property(assign) int
intAssign;
Ti,V_intSynthEquals
Ti,GintGetFoo,SintSetFoo:,VintSetterGe
tter
Ti,R,VintReadonly
Ti,R,GisIntReadOnlyGetter
Ti,VintReadwrite
Ti,VintAssign
T@,&,VidRetain
T@,C,VidCopy
Ti,VintNonatomic
T@,R,C,VidReadonlyCopyNonatomic
T@,R,&,VidReadonlyRetainNonatomic
@property(retain)
idRetain;
@property(copy) id idCopy;
id
@property(nonatomic) int
intNonatomic;
@property(nonatomic,
readonly, copy)
idReadonlyCopyNonatomic;
@property(nonatomic,
readonly, retain)
idReadonlyRetainNonatomic;
id
id
整理:http://www.aisidechina.com/forum/