2012-02-10 31 views
2

我有下面的代碼片段:iOS - 區分NSDictionary和NSMutableDictionary?

NSString* value = @"value"; 
NSString* key = @"key"; 
NSMutableDictionary* foo = [NSMutableDictionary dictionary]; 
NSDictionary* bar = [NSDictionary dictionary]; 
NSAssert([bar isKindOfClass: [NSMutableDictionary class]], @""); // passes the assert as both are NSCFDictionary; 
NSAssert([bar respondsToSelector: @selector(setValue:forKey:)], @""); // passes the assert because it NSCFDictionary does respond to the selector 
[foo setValue:value forKey:key]; // no problem, foo is mutable 
[bar setValue:value forKey:key]; // crash 

所以,如果我有一個對象,或者是一個NSDictionaryNSMutableDictionary,短try塊(我寧願避免)的,我怎麼能告訴區別?

+1

你可能想要使用-setObject:forKey:,而不是-setValue:forKey :. – 2012-02-10 23:06:35

+0

你有一個真實世界的例子,你什麼時候有一本字典,但不確定它是否可變? – bneely 2012-02-10 23:15:45

+0

只想添加[bar respondsToSelector:@selector(setValue:forKey :)]不起作用。 – picciano 2012-02-10 23:32:47

回答

3
NSAssert([bar isMemberOfClass: [NSMutableDictionary class]], @""); 
+2

適用於iOS 6+。在iOS 5.x及更低版本中失敗嚴重。在使用時請記住這一點。 – 2012-10-25 04:22:13

+0

只是擡起頭來,如果你在主體內部,這將失敗。改用NSCAssert。 – smileBot 2013-04-19 19:23:40

+0

這實際上是不正確的。 https://www.bignerdranch.com/blog/about-mutability/ – 2015-05-05 15:22:20

1

這其實很簡單,所有你需要做的是對類的成員測試,這樣的:

if ([bar isMemberOfClass:[NSMutableDictionary class]]){ 
    [bar setValue:value forKey: key]; 
} 

iPhone SDK difference between isKindOfClass and isMemberOfClass

+0

不幸的是,這實際上並不奏效。這是一個類集羣,你可以在這裏獲得'__NSCFDictionary',正如OP指出的那樣,這個測試失敗了。 – 2012-02-10 23:15:26

+0

正如我指出的鏈接所顯示的,OP作爲一個例子寫的內容和我寫的內容之間有區別。 OP使用'isKindOfClass:'檢查類的相等性,在這裏我檢查'isMemberOfClass:'。雖然我沒有測試過,但如果bar是一個'NSDictionary',它應該是'false'。看到這個鏈接更好的解釋:http://stackoverflow.com/a/3653948/267892 – Emil 2012-02-10 23:25:50

+0

然後,也許你應該測試它。 :)它不起作用 - 成員測試失敗NSDictionary和NSMutableDictionary,因爲既不是被檢查的對象的實際類。 – 2012-02-11 05:36:49

5

老實說,你不能輕易分辨出來沒有@try區塊。標準的做法如下:

  • 如果你想檢查它是否是爲了避免有人通過可變你意想不到的可變對象,然後從下你,copy字典變異出來。如果它真的是不可變的,框架將優化成retain。如果它真的可變的,那麼你可能想要一個副本。
  • 如果您需要一個可變字典,請聲明您的方法參數需要一個。就那麼簡單。
  • 一般來說,相信人們不會試圖改變對象,除非明確告訴他們是可變的。
0

如果你需要保證一個字典是可變的,你應該:

  • 創建爲可變的,如果你是一個初始化它
  • 把它變成一個可變的實例與 - [NSDictionary mutableCopy]方法,例如,如果您正在調用返回NSDictionary的API。請注意,mutableCopy遞增保留計數;如果使用手動引用計數,則需要釋放此實例。

確定一個NSDictionary是否試圖修改它的鍵和/或值之前,可變可以考慮code smell的例子,表明有應解決的深層次問題。換句話說,您應該始終知道給定的字典實例是NSDictionary還是NSM​​utableDictionary。代碼應該清楚。

+0

理論上不錯,但實際上這些情況確實發生。例如,假設我通過在某個數組上調用objectAtIndex:或者在某個字典上使用valueForKey:來獲取它。現在你可能會說我應該小心我輸入數組的內容。這是一個很好的理論,但因爲我不想包含這些檢查而導致崩潰的代碼不是一種選擇。 – 2012-02-11 01:30:05

+0

你不能總是知道。例如,在編寫單元測試時,即使您確實知道,但驗證方法返回的對象是否符合預期仍然是一個好主意。 – Caleb 2015-07-28 05:33:34

0

如果你真的不在乎,如果它是可變的,但無論如何要發生變異,你可以試試這個:

NSString* value = @"value"; 
NSString* key = @"key"; 

NSDictionary* bar = [NSDictionary dictionary]; 
NSLog(@"bar desc: %@", [bar description]); 
NSMutableDictionary* temp = [bar mutableCopy]; 
[temp setValue:value forKey:key]; 
bar = temp; 
NSLog(@"bar desc: %@", [bar description]); 
0

我最終使用@try抓,但它只能觸發一次。所以它的工作原理是這樣的

NSMutableDictionary *valuesByIdMutable = (id)self.valuesById; 

simplest_block block = ^{ 
    [valuesByIdMutable setObject:obj forKey:key]; 
}; 

@try { 
    block(); 
} @catch (NSException *exception) { 
    self.valuesById = valuesByIdMutable = [valuesByIdMutable mutableCopy]; 
    block(); 
} 
相關問題