让我们考虑下面的代码:
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