2010-01-19 33 views
7

這將產生一個不可變的字符串對象:Cocoa:測試以發現NSString是不可變的還是可變的?

NSString* myStringA = @"A"; //CORRECTED FROM: NSMutableString* myStringA = @"A"; 

這將產生一個可變的字符串對象:

NSMutableString* myStringB = [NSMutableString stringWithString:@"B"]; 

但是這兩個對象都報告爲同一類型的對象, 「NSCFString」:

NSLog(@"myStringA is type: %@, myStringB is type: %@", 
[myStringA class], [myStringB class]); 

那麼什麼是內部區分這些對象,以及如何測試,以便我可以輕鬆地確定一個神祕字符串變量是否是immut在做出對自己有害的事情之前能夠做到或做出改變?

+0

Philippe下面的代碼 - if([myStringB isKindOfClass:[NSMutableString class]]) - 可以解決實際問題。 – StringSection 2010-01-19 10:36:02

+0

我仍然對如何在內部表示不可變和可變字符串之間的差異感到好奇,以及是否可以直接檢測到(實際「NSLog打印的對象類型」)。 – StringSection 2010-01-19 10:36:44

+1

更正:我錯了,代碼 - if([myStringB isKindOfClass:[NSMutableString class]]) - 畢竟不工作。無論字符串是NSString還是NSM​​utableString,它都會返回true。正如菲利普在下面所指出的(在他現在編輯的不同代碼的答案中),顯然沒有書面的方式來在運行時檢測可變與不可變對象。 – StringSection 2010-01-19 11:33:46

回答

4

有沒有(記錄)的方式來確定一個字符串在運行時是否可變。

你會以爲,以下將工作之一,但他們沒有工作:

[[s class] isKindOfClass:[NSMutableString class]]; // always returns false 
[s isMemberOfClass:[NSMutableString class]]; // always returns false 
[s respondsToSelector:@selector(appendString)]; // always returns true 

這裏更多的信息,雖然它並不能幫助你的問題:

http://www.cocoabuilder.com/archive/cocoa/111173-mutability.html

+0

不起作用:NSMutableString * lala = @「lala」; \t if([lala isKindOfClass:[NSMutableString class]])NSLog(@「Mutable class」); - 顯示日誌消息,但是對象是不可變的 – Vladimir 2010-01-19 10:17:50

+0

當然,它不起作用,因爲代碼是錯誤的。 ** NSMutableString * lala = @「lala」**是非法代碼。 – 2010-01-19 10:20:11

+0

對不起,你們 - NSMutableString * myStringA = @「A」;原來的信息裏現在是一個錯字 - 現在糾正了。 – StringSection 2010-01-19 10:21:25

3

如果你想檢查調試目的,下面的代碼應該工作。在不可變對象上覆制本身,而它是可變類型的真正副本,這就是代碼所基於的。請注意,由於它調用了copy,所以速度很慢,但對於調試應該沒問題。如果你想檢查除調試以外的任何其他原因,請參閱Rob的回答(並忘記它)。

BOOL isMutable(id object) 
{ 
    id copy = [object copy]; 
    BOOL copyIsADifferentObject = (copy != object); 
    [copy release]; 
    return copyIsADifferentObject; 
} 

聲明:當然不能保證複製與不可變類型的保留等價。你可以確定,如果isMutable返回NO,那麼它不可變,所以函數應該可能被命名爲canBeMutable。然而,在現實世界中,這是一個非常安全的假設,即不可變類型(NSString,NSArray)將實現這種優化。有很多代碼包括像NSDictionary這樣的基本事物,它們需要從不可變類型快速複製。

12

該文檔包含一個相當長的解釋,爲什麼蘋果不希望你這樣做,爲什麼他們明確不支持Receiving Mutable Objects。總結是:

所以千萬不要依據是什麼內省 告訴你一個對象對象 可變性的決定。將 對象視爲可變或不可變根據 您在API邊界處(即基於 返回類型)交給的內容。如果您需要將 明確標記爲 當您將其傳遞給客戶端時爲可變或不可變,請將該信息作爲 標誌與對象一起傳遞。

我發現他們的NSView示例最容易理解,它說明了一個基本的可可問題。你有一個名爲「元素」的NSMutableArray,你想以數組的形式公開,但不希望調用者混淆。您有以下幾種選擇:

  1. 將您的NSMutableArray公開爲NSArray。
  2. 請求時請始終制作不可變的副本
  3. 將元素存儲爲NSArray,並在每次變化時創建一個新數組。

我已經完成了所有這些在不同的點。 #1是迄今爲止最簡單和最快的解決方案。這也是危險的,因爲數組可能會在呼叫者的背後變異。但蘋果表示這是他們在某些情況下做的事情(請注意NSView中的-subviews的警告)。我可以證實,雖然#2和#3更安全,但他們可能會造成主要的性能問題,這可能就是爲什麼蘋果選擇不將它們用於像-subviews這樣的經常訪問的成員。

所有這些的結果是,如果你使用#1,那麼內省會誤導你。你有一個NSMutableArray強制類型轉換爲NSArray,並且內省將表明它是可變的(內省沒有辦法知道)。但是你不能改變它。只有編譯時類型檢查可以告訴你,所以它是你唯一可以信任的東西。

此問題的解決辦法是某種可變數據結構的快速複製寫入不可變版本。這種方式#2可能可以完成體面的表現。我可以想象對NSArray集羣的更改會允許這樣做,但它現在在可可中不存在(並且可能會影響正常情況下的NSArray性能,使其成爲非啓動器)。即使我們擁有它,也可能有太多的代碼依賴於當前的行爲來允許可變性內省被信任。

+1

您可以通過實現並暴露變異數組訪問器(http://developer.apple.com/documentation/Cocoa/Conceptual/ModelObjects/Articles/moAccessorMethods.html)並在其他類中使用它們來解決性能問題。這樣,直接訪問可變數組仍然是私有的,但是您不會創建並丟棄臨時數組。 – 2010-01-20 04:16:46

+1

@PH這是真的,我應該已經涵蓋了KVC解決方案。它帶來的問題是,在很多情況下,調用者確實需要一個集合(通常是傳遞給其他需要的東西)。我經常通過暴露諸如elementEnumerator之類的東西來解決這個問題,如果調用者願意,它可以變成快照。這種方法的問題是調用者和被調用者都需要額外的代碼量。令人啓發的是,即使在新的UIView代碼中,Apple也選擇將NSMutableArray「子視圖」作爲NSArray公開,而不是提供完整的KVC。 – 2010-01-20 14:16:12

+0

嗯?我不清楚你在說什麼問題或你的解決方案是什麼。博客文章和/或pastebin? – 2010-01-21 02:05:55

相關問題