讓我們考慮下面的代碼:
if ([dict isKindOfClass:[NSMutableDictionary class]])
{
[(NSMutableDictionary*)dict setObject:@1 forKey:@"1"];
}
我認爲蘋果的音符問題引述的真正目的是,這種代碼很容易導致崩潰。這裏的問題在於與CoreFoundation進行免費橋接。讓我們更詳細地考慮4個變種:
NSDictionary* dict = [NSDictionary new];
NSMutableDictionary* dict = [NSMutableDictionary new];
CFDictionaryRef dict = CFDictionaryCreate(kCFAllocatorDefault, NULL, NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
確實,在所有4個變種中,dict的真實類將是__NSCFDictionary。所有4個變體都將通過第一個代碼片段中的測試。
*終止應用程序由於未捕獲的異常 'NSInternalInconsistencyException',原因是::但2例(NSDictionary中和CFDictionaryRef聲明),我們將與日誌類似的東西崩潰「 - [__ NSCFDictionary的setObject:forKey:]:變異的方法發送到不可變的對象'
所以,事情變得更清晰一點。在2種情況下,我們可以修改對象,2種情況下不允許修改對象。它依賴於創建函數,可能的可變性狀態由__NSCFDictionary對象本身來跟蹤。
但是,爲什麼我們使用相同的類爲可變和不可變的對象? 可能的答案 - 因爲C語言的限制(並且CoreFoundation是一個C API,正如我們所知)。我們在C中遇到什麼問題?可變和不可變字典類型的聲明應該反映如下:CFMutableDictionaryRef類型是CFDictionaryRef的子類型。但是C沒有這個機制。但是我們希望能夠在CFDictionary對象的函數中傳遞CFMutableDictionary對象,並且最好不用編譯器發出惱人的警告。我們必須做什麼?
讓我們來看看下面的CoreFoundation類型聲明:
typedef const struct __CFDictionary * CFDictionaryRef;
typedef struct __CFDictionary * CFMutableDictionaryRef;
正如我們所看到的,CFDictionary和CFMutableDictionary由同一類型表示,只有在const修飾不同。所以,這裏開始麻煩。
這同樣也適用於NSArray,但情況稍微複雜一點。當你直接創建NSArray或NSMutableArray時,你會得到一些特殊的類(分別是__NSArrayI和__NSArrayM)。對於這個類的崩潰沒有轉載。但是,當你創建CFArray或CFMutableArray時,事物保持不變。所以要小心!
您在3年的時間裏對此有所瞭解嗎?什麼樣的愚蠢的`NSMutableArray`子類不會是可變的? – 2011-08-21 20:13:49
從實踐經驗來看,它並不重要;正如你所說我只見過可變的NSMutableArrays,而且我不認爲我曾經使用過一個子類(至少有一個是由第三方製作的)。 Objective-C的大部分內容都是關於不明確的約定,我認爲您可以確定您可能遇到的任何NSMutableArray子類的行爲方式對其他任何Objective-C程序員都有意義。另外,我還要補充一點,我沒有遇到過必須做這樣的檢查的情況 - 任何有數據流的路徑應該總是可變的或不可變的。 – 2011-08-21 20:19:25
同意,當你閱讀API時會遇到一些附帶的問題,但很少在野外。對我來說,這屬於「不要寫`if'這個'else'條件沒有意義的大類'或類似的東西。 – 2011-08-21 22:04:40