2011-05-05 54 views
15

我在Objective-C的MacOS-X 10.6.7和Xcode 4.0.2中使用respondsToSelector方法來識別對象是否會響應某些消息。根據手冊,NSString不應該響應appendString:而NSMutableString應該。下面是該測試中,它的代碼段:爲什麼NSString響應appendString?

int main (int argc, const char * argv[]) 
{ 

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 
    NSString *myString = [[NSString alloc] init]; 

    if ([myString respondsToSelector:@selector(appendString:)]) { 
     NSLog(@"myString responds to appendString:"); 
    } else { 
     NSLog(@"myString doesn't respond to appendString:"); 
    } 

    // do stuff with myString 

    [myString release]; 
    [pool drain]; 
    return 0; 
} 

和這裏的輸出:

Class02[10241:903] myString responds to appendString: 

我已經有點預期相反。 NSString對象如何響應appendString:?這裏發生了什麼,我錯過了?

+0

如果你發送了一個真正的'appendString:'消息,是否有什麼東西炸燬? – BoltClock 2011-05-05 08:55:02

+1

是的:'試圖用appendString:'來改變不可變的對象。 – zoul 2011-05-05 08:56:57

+0

@BoltClock:是的,它爆炸了,但不是使用'respondsToSelector:'方法來檢查在發送真正的'appendString:'消息之前它不會炸燬嗎? – user698769 2011-05-05 09:00:08

回答

1

你不應該對被有一個方法假設。該方法可能會在內部使用,或出於任何原因存在。從技術上講,這只是私人API。

您只與公開聲明有關的合同(文檔),並且它們不顯示該消息。因此,如果您使用其他功能,請準備好很快陷入困境。

+0

同意,但有人可能會爭辯說,支持只拋出異常的方法很奇怪。它反對鴨子打字 - 這個特別的東西走路像鴨子,但不是鴨子。 – zoul 2011-05-05 09:08:31

+0

@zoul:我見過'NotImplementedException'拋出其他語言的地方。如果在這裏發生類似事情,我不會感到驚訝。但你的回答似乎更明智。 – BoltClock 2011-05-05 09:09:46

2

這可能與實施有關。 NSString是一個類集羣,這意味着NSString只是一個公共接口,實際的實現類是不同的(see what the class message gives you)。

,並在同一時間NSString也是免費電話橋接CFString,這意味着你可以自由只是鑄造這兩種類型前切換:

NSString *one = @"foo"; 
CFStringRef two = (CFStringRef)one; // valid cast 

當你創建一個新的字符串你真的得到了NSCFString回來,一個薄薄的包裝CFString。關鍵是,當你創建一個新的可變字符串時,你也會得到一個NSCFString的實例。

Class one = [[NSString string] class]; // NSCFString 
Class two = [[NSMutableString string] class]; // NSCFString 

我想這是從實施情況來看方便 - 無論NSStringNSMutableString可以通過一個共同的類(=更少的代碼複製)和這個類進行備份可以確保不違反不變性:

// 「Attempt to mutate immutable object with appendString:」 
[[NSString string] appendString:@"foo"]; 

在這個答案中有很多猜測工作,我不太瞭解這些東西,讓我們希望有人知道更好。

10

簡短回答:該字符串的類型爲NSCFString,它繼承自NSMutableString,因此它響應NSMutableString中聲明的方法(包括超類)的選擇器。

不是那麼簡單的答案:基礎字符串是免費橋接與核心基礎字符串。開發人員使用不透明類型CFStringRef(橋接NSString)和CFMutableStringRef(橋接NSMutableString)來引用這些字符串,因此乍一看有兩種不同類型的字符串:不可變和可變的。

從Core Foundation內部實現的角度來看,有一個叫做struct __CFString的私有類型。這個私有類型保留一個位字段,用於存儲字符串是可變的還是不可變的。擁有單一類型可以簡化實現,因爲許多函數都由不可變和可變字符串共享。

每當調用對可變字符串進行操作的核心基礎函數時,它首先讀取該位字段並檢查該字符串是可變的還是不可變的。如果參數應該是可變字符串,但實際上不是,則函數返回錯誤(例如_CFStringErrNotMutable)或斷言失敗(例如__CFAssertIsStringAndMutable(cf))。

無論如何,這些都是實施細節,他們將來可能會改變。 NSString未聲明-appendString:這一事實並不意味着每個NSString實例都不會響應相應的選擇器 - 請考慮substitutability。同樣的情況適用於其他可變/不可變類,例如NSArrayNSMutableArray。從開發人員的角度來看,重要的是返回的對象的類型與返回類型相匹配 - 它可以是類型本身或該類型的任何子類型。類羣使這一點變得更加複雜,但情況並不侷限於類集羣本身。

總之,您只能期望方法返回一個對象,該對象的類型屬於返回值類型的層次結構(即類型本身或子類型)。不幸的是,這意味着你不能檢查基礎對象是否是可變的。但是再一次,你真的需要這張支票嗎?


您可以使用CFShowStr()函數從字符串中獲取信息。在你的問題的例子,添加

CFShowStr((CFStringRef)myString); 

你應該得到類似的輸出:

Length 0 
IsEightBit 1 
HasLengthByte 0 
HasNullByte 1 
InlineContents 0 
Allocator SystemDefault 
Mutable 0 
Contents 0x0 

其中

Mutable 0 

意味着該字符串實際上是不可改變的。

+0

這是我期待的答案,謝謝! – zoul 2011-05-05 10:29:47