Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> runtime從入門到精通(二)—— 官方文檔翻譯

runtime從入門到精通(二)—— 官方文檔翻譯

編輯:關於Android編程

本文把runtime的官方文檔給大家翻譯過來,官方文檔的語言比較晦澀難懂,但是我們還是要在正式學習之前閱讀以下,有些名詞不懂不要緊哦,接著往下讀。

前言

OC是一種面向對象的動態語言,作為初學者可能大多數人對面向對象這個概念理解的比較深,而對OC是動態語言這一特性了解的比較少。那麼什麼是動態語言?動態語言就是在運行時來執行靜態語言的編譯鏈接的工作。這就要求除了編譯器之外還要有一種運行時系統來執行編譯等功能。OC中這個系統就是runtime。

一、版本

OC中的運行時分為兩個版本——Modern Runtime和Legacy Runtime。現在的運行時遺留的運行時區別在於:遺留的運行時在改變一個類的結構時,你必須繼承它並重新編譯。而現在的運行時可以直接編譯。

iPhone應用程序和64程序在OX v10.5和以後使用現在版本的運行時。其他項目的使用的都是遺留版運行時。

OC程序與運行時系統交互分為三個不同等級:

通過OC源代碼 通過定義在Foudation框架中NSObject中的方法 通過直接調用運行時的函數

1. 通過OC源代碼

在大多數情況下,運行時會自動在幕後工作。你使用它只是編寫和編譯OC源代碼。

當你編譯的代碼包含OC中的類和方法時,編譯器創建數據結構和函數調用,實現語言的動態特性。數據結構捕獲類,分類和協議中聲明的信息。其中包括在OC中討論類和協議對象的定義,以及從源代碼中提取出來方法選擇器,實例模板和其他信息。運行時的主要功能就是傳遞消息,正如消息傳遞中所描述的那樣。它通過源代碼消息表達式來來調用。

2. 通過NSObject中定義的方法

在Cocoa中,大多數對象是NSObject類的子類對象,所以大多數對象繼承了他定義的方法(NSProxy類除外)。因此它的方法建立每個實例,每個類對象的行為。然而在少數情況下,NSOject只定義了一個怎樣去做的模板,它本身不提供所有必要的代碼(抽象類?)

例如,NSObject定義了一個返回一個描述類內容的字符串的實例方法。這主要用於調試GDB對象打印命令從這各類中打印的字符串。NSObject的方法實現中不知道類中包含什麼內容,所以它返回一個包含對象名和地址的字符串。NSObject的子類可以實現這個方法返回更多的細節。例如,Foundation中NSSArray返回一個它包含對象的描述列表。

NSObject方法的一些簡單的查詢的運行時系統信息。這些方法允許對象自省(自我查找)。這種方法的例子是類方法,例如isKindOfClass:問一個對象來確定它的類:isMemberOfClass測試對象在繼承結構中的層次位置,respondsToSelector,這表明一個對象是否能接受特定的消息,conformsToProtocol:確定對象是否實現在特定協議中定義的方法,methodForSelector:提供方法實現的地址。像這樣的方法給予了對象自省的能力。

3. 直接調用運行時的函數

運行時系統是一個定義在/usr/include/objc目錄下的,有一個公共接口在它頭文件中包含一系列方法和數據結構動態共享庫。這裡面許多方法允許你使用C語言重復編譯器在你寫OC代碼時是怎樣工作的。其他基礎功能形式通過NSObject類的方法來導出。當OC中不需要時,這些方法使開發runtime的其他接口,生產出增強開發環境的工具成為可能。然而,一小些運行時函數只能在編寫OC程序時有用。所有的功能都記錄在Objective-C Runtime Reference.中。

二、消息傳遞機制

這一部分描述了如何把消息表達式轉換成objc_msgSend函數調用,怎樣通過名字找到方法。然後解釋了如果你需要的話怎麼通過objc_msgSend來繞過動態綁定。

1. objc_msgSend 功能

在OC中,消息不跟方法實現綁定直到運行時。編譯器將消息表達式 [receiver message] 轉化成一個消息傳遞函數objc_msgSend。這個函數將接收者和在消息中提到的方法名(方法選擇器)作為他的兩個主要參數:objc_msgSend(receiver, selector)。消息中任何參數也交給objc_msgSend:objc_msgSend(receiver, selector, arg1, arg2, …)。

消息傳遞函數為動態綁定做了所有必須的事情:

它首先發現方法選擇器指向的程序(方法的實現)。因為相同的方法可以被不同的類分別實現。這個准確的程序依賴於接收者的類。

然後調用程序,通過接收對象(指針指向他的數據)為方法傳遞指定的參數。

最後,當他返回值的時候它傳遞程序的返回值。

提示:編譯器對消息傳遞函數生成調用,在你的代碼中不要直接調用。

消息傳遞機制的關鍵在於編譯器對每個類和對象的結構的構建,每個類結構包含兩個基本元素:指向父類的指針和類調度表。這個表羅列了他們定義的有明確類特征的方法的地址的方法選擇器。例如,setOrigin::方法的選擇器與setOrigin::方法的實現聯系起來,展示方法的選擇器關聯展示的地址等等。

創建新對象時,分配內存,實例變量被初始化。首先在對象中有一個指向它的類結構的指針變量。這個指針被稱為isa指針,它使對象能夠訪問類,通過類可以訪問它繼承的所有的類。

注意:雖然不是嚴格意義上語言的一部分,isa指針需要一個對象運行在OC運行時系統。一個對象需要等效的objc_object結構體無論是定義在這個結構的任意字段。然而,你很少甚至從來不需要創建你自己的根對象,繼承自NSObject 或者 NSProxy的對象自動擁有可變的isa指針。

這些類的元素和結構如下圖:

類的元素和結構

當一個消息傳遞給一個對象的時候,消息函數沿著這個對象的isa指針在調度表找到它建立起方法選擇器的類結構。如果它不能在這裡發現選擇器,obic_msgSend根據指針找到它的父類,在父類的調度表中尋找選擇器。連續失敗導致objc_msgSend沿著類繼承結構直到尋找到NSObject類。一旦確定選擇器的位置,函數調用表中的方法並且把它傳給接收對象的數據結構。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPtXivs3Kx9TL0NDKsbe9t6jRodTxyrXP1rXE0aHU8be9t6ijrNTaw+bP8rbUz/O1xLHgs8zK9dPv1tDO0sPHv8nS1Mu1t723qLrNz/vPosrHtq/MrLDztqi1xKGjPC9wPg0KPHA+zqrBy7zTy9nP+8+itKu13bn9s8yjrNTat723qLG7yrnTw8qxo6zUy9DQyrHPtc2zu7q05sHLt723qLXE0aHU8cb3us212Na3oaPDv7j2wOC2vNPQ0ru49rWltsC1xLu6tOajrMv8sPy6rMHLvMyz0LXEt723qLrN19S8usDg1tC2qNLltcS3vbeotcTRodTxxveho9TasunV0rX3tsix7daux7CjrM/7z6LA/dDQs8zQ8srXz8i74dTavdPK1dXfttTP87XEwOC1xLu6tObW0LLp1dKho6OowO3C28nPwLTLtaOs08O5/dK7tM61xLe9t6i63L/JxNzU2bTOsbvKudPDo6nI57n7t723qNGh1PHG99Tau7q05sDvw+ajrM/7z6K0q7Xd1ru74bHIuq/K/bX308PC/dK7teOho8jnufvSu7j2s8zQ8tTL0NC1xNfjubuzpLXEysK8/sC0JmxkcXVvO8jIye0mcmRxdW87u7q05qOsvLi69cv509C1xMv7t6LLzbXEz/vPor/J0tTV0rW90ru49ru6tOa1xLe9t6iho7Wxs8zQ8tTL0NDKsaOsu7q05rj5vt3Qwreiy821xM/7z6K2r8ys1PazpKGjPC9wPg0KPGg0IGlkPQ=="2-使用隱藏參數">2. 使用隱藏參數

當objc_msgSend找到一個方法的實現程序,它調用這個程序,傳遞消息中的所有參數。它也傳遞給程序兩個隱藏參數:接收對象和方法選擇器

這些參數給了每個方法實現關於調用它的兩部分消息表達的明確信息,它們被說成隱藏的是因為它們在定義方法的源代碼中沒有聲明。當代碼被編譯的時候它們被插入實現中。

雖然這些參數沒有被顯式聲明,源代碼仍然可以引用他們(就像它可以接收實例變量一樣)一個方法引用接收對象作為自己,引用他自己的方法選擇器作為_cmd。在下面的實例中,_cmd引用strange方法的選擇器,自己作為strange消息的接收對象。

- strange
{
    id  target = getTheReceiver();
    SEL method = getTheMethod();

    if ( target == self || method == _cmd )
        return nil;
    return [target performSelector:method];
}

Self比兩個參數更有用。事實上,這是接收對象的實例變量提供了方法的定義方式。

3. 獲取方法地址

為了避免動態綁定的唯一方法是得到一個方法的地址,當他是函數的時候直接調用。這可能是極少數的情況下是合適的,當一個特定的方法陸續執行了很多次,你想節省每次方法調用時的開銷。

一個定義在NSObject中的方法,methodForSelector:,你可以要求一個指針指向它,然後通過指針來調用他。methodForSelector:這個指針必須返回正確的函數類型。同時返回值和參數的類型也應該包含在內。

下面的例子展示實現setFilled:方法的程序可能是如何被調用的:

void (*setter)(id, SEL, BOOL);
int i;

setter = (void (*)(id, SEL, BOOL))[target
    methodForSelector:@selector(setFilled:)];
for ( i = 0 ; i < 1000 ; i++ )
    setter(targetList[i], @selector(setFilled:), YES);

首先兩個參數傳遞給接收對象是self方法選擇器是_cmd的程序。這些參數被隱藏在方法的語法中但是在這個方法作為一個函數調用的時候必須明確。

使用methodForSelector:規避動態綁定可以節省大多數信息傳遞的時間。然而,只有當一個特定的方法執行很多次的時候節省的消耗才比較明顯,就像上面for循環所示。

注意:methodForSelector:是運行時系統提供的而不是OC的特點。

三、動態方法解析

這一章講述了你可以動態的提供一個方法的實現

1. 動態方法解析

有某種情況下,你可能需要動態地為你的方法提供實現。比如,這個OC聲明屬性中包含@dynamic指令的時候:

@dynamic propertyName;

它告訴編譯器與屬性相關聯的方法將動態提供。

你可以實現方法resolveinstancemethod:和resolveclassmethod:分別為實例和類方法提供一個選擇器。

OC方法是一個至少包含self和_cmd兩個參數的C函數。當一個方法使用class_addMethod函數的時候可以為一個類添加函數。因此,給了以下函數:

void dynamicMethodIMP(id self, SEL _cmd) {

// implementation ....

}

你也可以把它作為一個方法添加到一個類中(調用resolveThisMethodDynamically)就像這樣:

@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;

2. 動態加載

一個OC在它運行的時候可以加載鏈接很多類和分類。加入的新代碼和一開始加載的類和分類做相同處理。

動態加載可以用來做很多不同的事情。比如在系統偏好設置的各個模塊中動態加載。

在Cocoa中,動態加載經常被用於程序定制。別人修改寫你在運行時加載的程序,比如說當界面生成器加載自定義調色板和OS X系統偏好設置自定義模塊加載應用程序的偏好的時候。加載模塊擴展你的應用程序。他們有助於你允許但沒有預計或者定義。你可以提供框架別人提供代碼。

即使runtime函數提供了在Objective-C Mach-O文件動態加載模塊,然而Cocoa的NSBundle類提供了一個面向對象的動態加載和相關服務集成更方便的接口。可以在Foudation框架引用中查找NSBulde的詳細說明和它如何讓使用。

四、消息轉發

向不處理該消息的對象發送一個消息是一個錯誤。然而,在宣布錯誤之前,運行時系統給接收對象一個第二次機會來處理消息。

1. 轉發

如果你給一個不處理這個消息對象發送消息,在認識到時一個錯誤之前運行時會給對象發送一個帶有NSInvocation對象作為唯一參數的forwardInvocation:消息。這個NSInvocation封裝了原始的消息,參數通過它傳遞。

你可以通過實現forwardInvocation:方法來指定一個默認的響應或者通過其他方式來避免這個錯誤。正如它的名字按時的那樣,forwardInvocation:通常用於抓發消息給另一個對象。

要查看轉發的范圍和意圖,你可以想象以下情況:首先,你假設你正在設計一個可以響應談判消息的對象,並且他可以響應另外一種對象的響應。你可以輕易地通過發消息給另外一個包含你實現談判方法的對象來實現。

進一步說,你想你的對象對於談判消息的精確的在另外一個類中響應。實現這一方法的方式是讓你的類繼承於別的類的方法。然而,它不可能通過這種方式來安排事情。這有很多好的為什麼你的類和實現了談判的類在繼承結構的不同分支的原因。

即使你的類不能繼承談判方法,你也可以通過實現一個簡單傳遞給另一個類的實例消息的方法中的一個版本來“借用”它:

- (id)negotiate
{

    if ( [someOtherObject respondsTo:@selector(negotiate)] )

        return [someOtherObject negotiate];

    return self;

}

這種方式可能有點麻煩,特別是當你希望你的對象傳遞一些消息給另外一個對象的時候。你不得不實現每個你想從其他類中借用的方法。然而,在你寫代碼的時候你不可能處理你不知道所有你想要轉發的消息的集合的情況。這個集合可能依賴於運行時中的事件,也可能在將來新實現類和新方法的時候改變。

forwardInvocation:消息提供了第二個機會:另外一個不是那麼特別的解決方案,是動態而不是靜態。它是像這樣工作的:當一個對象因為沒有這個消息對應的方法選擇器來響應這個消息。運行時系統通過發forwardInvocation:消息通知對象。每個對象都從NSObject類中繼承了一個forwardInvocation:方法。然而,NSObjcet類中的方法版本只是僅僅調用了doesNotRecognizeSelector:。通過重寫NSObject類實現的你自己的版本,forwardInvocation:消息提供想另一個對象轉發消息的時候抓住這個機會。
forwardInvocation:轉發消息時所有該做的事情是:1.確定消息要傳到哪2.帶著原始參數把它發送過去。
消息會隨著invokeWithTarget:方法發送:

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    if ([someOtherObject respondsToSelector:
            [anInvocation selector]])
        [anInvocation invokeWithTarget:someOtherObject];
    else
        [super forwardInvocation:anInvocation];
}

轉發消息的返回值返回給原始發送者。所有類型的返回值都可以傳遞給發送者,包括id類型,結構體,單精度和雙精度浮點數。

forwardInvocation:像一個為無法識別消息工作的分配中心,把他們打包到不同的接收器。也可以作為一個中轉站,把所有信息發送到一個目的地。他可以轉運一些消息到其他地方,也可以“吞食”一些方法,所以這裡沒有響應和錯誤。 forwardInvocation:也可以把幾條消息合並到一個響應中。 forwardInvocation:做的是把上交給實現者。然而,它為在轉發鏈上上的連接對象打開了程序設計的可能。

**注意:**forwardInvocation:方法只能處理那些名義上沒有存在調用方法的消息。例如,你想要你的對象轉發談判消息給另外一個對象,它不能有自己的談判方法。如果有,消息永遠不會到達nominal receiver。

2. 轉發和多繼承

轉發模擬繼承,可為OC程序提供多繼承效果,如下圖所示,一個對象響應一個消息可以通過借用或者繼承其他類的方法實現

在本示例中,戰士類的一個實例將談判消息轉發到外交官類的一個實例。談判的戰士將會出現像一個外交官。似乎將應對談判信息,實際上它回應(盡管它真的是一個外交官做的工作)

轉發消息的對象因此“繼承”來自兩個繼承層次結構的方法,一個是自己的分支,另一個是響應這個消息的對象。在上面的示例中,這看起來就像是戰士類繼承自外交官以及自己的超類。

轉發提供了大多數你想從多繼承活的功能。然而,兩者之間最大的區別在於:多繼承是結合不同的功能在一個對象中。它傾向於大的,多方面的對象。另一方面,轉發機制將不同的功能分配給不同的對象。它把大的問題分解成小的對象,但是通過對消息發送者透明來把這些對象關聯起來。

3. 代理對象

轉發不僅模仿多繼承,它也使開發輕量級的代表或者“覆蓋”更大量的對象的對象。代理就代表了其他的對象,篩選傳遞給他的消息。

在OC編程語言中的遠程通信中是這樣一個代理。代理需要照顧轉發到遠程接收者的消息的管理細節,確保通過連接的參數值被復制和檢索等等。但它並沒有嘗試去做其他的事情;它不復制遠程對象的功能,只是給給遠程對象一個本地但它並沒有嘗試去做其他的事情;它不復制遠程對象的功能,但只要給遠程對象一個可以在另一個應用程序中接收消息的本地地址。

其他類型的代理對象也可能。例如,假設你有一個對象,操縱大量數據,也許它創建了一個復雜的圖像或讀取磁盤上的文件的內容。設置這個對象是費時的,所以你喜歡懶加載它,當它真正需要的時候或當系統資源暫時閒置的時候。同時,你需要至少一個占位符對象,其他對象在應用程序正常運行。

在這種情況下,你可以創建一個輕量級的不完整的對象替代他。這個對象可以做到一些相對的事情,比如說回答關於數據的問題,但是大多數情況下,它僅僅為一個大對象占位置,當時間到了,轉發消息給它。這個代理的forwardInvocation:方法第一次接收到目的地為另一個對象的消息,他會確定這個對象是否存在,如果不存在就創建它。所有的大對象的消息都是通過代理,就程序的其他部分來說,代理和大對象是一樣的。

4. 轉發和繼承

雖然轉發模擬繼承,但是NSObject類從來不會混淆兩者。像respondsToSelector: 和isKindOfClass:這樣的方法只查看結構,從來不在轉發鏈上。例如,如果一個戰士對象被問到它是否會對談判信息作出反應:
if ( [aWarrior respondsToSelector:@selector(negotiate)] )
答案是不會,即使在某種意義上它可以通過轉發給一個外交官沒有錯誤地接收談判消息,並響應它,在大多數情況下,不是正確答案。但也有可能不是。如果你使用轉發來設置代理對象或者擴展一個類的功能,轉發機制可能是像繼承一樣透明。如果你想你的對象像他們真正繼承他們轉發消息的對象的行為一樣,你需要在respondsToSelector: 和isKindOfClass:中重新實現你的轉發算法。

- (BOOL)respondsToSelector:(SEL)aSelector
{
    if ( [super respondsToSelector:aSelector] )
        return YES; 
    else {

    } 
    return NO; 
}

除了respondsToSelector: 和isKindOfClass:方法之外,instancesRespondToSelector:方法中也應該復制轉發算法。如果使用協議,conformstoprotocol:方法也應該被添加到列表中。同樣,如果一個對象轉發任何它接收到的遠程消息,它應該有一個可以返回最終響應轉發消息的methodsignatureforselector:的該寫版。例如,如果一個對象能夠將消息轉發給它的代理,你會實現methodsignatureforselector:如下:

- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector 
{
    NSMethodSignature* signature = [super methodSignatureForSelector:selector];

    if (!signature) {
       signature = [surrogate methodSignatureForSelector:selector];
    }

    return signature;
}

你可能會考慮將轉發算法封裝到某個地方,讓所有方法包括forwardInvocation:調用他。

注意:這是一門先進的技術,僅僅是用於沒有別的解決方案。不是作為繼承的替代品。如果你必須使用這個技術,確保你對轉發消息的類和要轉發的類的行為有充分的了解。

五、類型編碼

為了幫助運行時系統,編譯器將每個方法中的返回和參數類型進行編碼,並將該字符串與該方法選擇器關聯。在其他情況下,編碼體系也是很有用的,所以編碼體系是帶有@encode()編譯指令的工公共的可用的。當給一個指定類型,@encode()返回指定的類型的字符串編碼。這個類型可以是任何類型,可以是基本類型,如int型指針,可以是一個標記結構或聯合,或類名,可以被C語言的sizeof()運算符作為參數使用。

下面的表格列出了編碼類型。注意當對一個對象歸檔或者分發時,他們中的許多代碼與你使用的代碼重疊。然而,這些列表中的編碼在你歸檔的時候不能使用他們,你可能想要在歸檔使用那些不是@encode()生成的代碼。

重要提示:OC不支持long double類型。@encode(long double)返回跟編碼double一樣返回d。
數組類型的編碼是包括方括號在內。數組中的元素數目在打開括號之後立即指定,在數組類型之前。例如,一個指向12個float類型的數組將被編碼成:

[12^f]

結構體在大括號內定義,聯合體在遠括號內定義。結構體的標簽首先被列出,然後一個等號和結構域的編碼順序列出。例如下面這個結構體:

typedef struct example {
    id   anObject; 
    char *aString; 
    int  anInt;
} Example;

將會編碼成

{example=@*i}

如果定義類型為(Example)或者(example)經過@encode()將會得到相同的編碼結果。結構指針的編碼攜帶相同數量的結構域的信息:

^{example=@*i}

然而間接尋址去除了內部類型的詳細描述
對象被視為結構。例如,通過NSObject類名稱@ encode()方產生這種編碼:{NSObject=#}
一個類只聲明一種isa指針變量
注意:當他們在協議中聲明方法的時候,即使@encode()命令不返回他們,運行時系統使用下表中的補充的編碼。

聲明屬性

當編譯器遇到屬性聲明,它生成與外圍類,分類和協議相關的描述性元數據。你可以使用支持通過名字查看類,分類,協議中的屬性的方法來查看這個元數據,獲得這個屬性的@encode字符串類型,復制成一個C語言字符串數組屬性屬性列表。聲明屬性的列表可用於每個類和協議。

六、聲明屬性

當編譯器遇到屬性聲明,它生成與外圍類,分類和協議相關的描述性元數據。你可以使用支持通過名字查看類,分類,協議中的屬性的方法來查看這個元數據,獲得這個屬性的@encode字符串類型,復制成一個C語言字符串數組屬性屬性列表。聲明屬性的列表可用於每個類和協議。

1. 屬性類型和方法

屬性結構定義一個屬性描述符的不透明句柄。

typedef struct objc_property *Property;

你可以使用class_copyPropertyList和protocol_copyPropertyList分別檢索與類,加載分類和協議相關的屬性數組:

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

你可以得到他的屬性列表:

id LenderClass = objc_getClass("Lender");
unsigned int outCount;
objc_property_t *properties = class_copyPropertyList(LenderClass, &outCount);

你可以使用property_getName函數發現屬性的名稱

const char *property_getName(objc_property_t property)

你可以在一個類或協議中指定一個名字,可以使用class_getProperty和protocol_getProperty分別獲得引用。

objc_property_t class_getProperty(Class cls, const char *name)

objc_property_t protocol_getProperty(Protocol *proto, const char *name, BOOL isRequiredProperty, BOOL isInstanceProperty)

你可以使用property_getAttributes這個函數去獲得屬性的名字和編碼字符串。了解編碼類型字符串詳情,看類型編碼,了解字符串詳情,看屬性字符串類型和屬性描述的例子:

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));
}

2. 屬性類型字符串

你可以使用property_getAttributes這個函數去獲得屬性的名字和編碼字符串,和一些其他屬性。

字符串以T打頭後面跟著編碼類型和逗號,結束是以V打頭加上返回實例變量的名字。在兩者中間以逗號隔開

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved