当前位置:  开发笔记 > 编程语言 > 正文

使用isKindOfClass:对NSString实例确定类型是否安全?

如何解决《使用isKindOfClass:对NSString实例确定类型是否安全?》经验,为你挑选了4个好方法。

来自NSObject中的isKindOfClass:方法文档:

在类集群表示的对象上使用此方法时要小心.由于类集群的性质,您获得的对象可能并不总是您期望的类型.

然后,文档继续给出一个示例,说明为什么不应该像NSArray实例的以下内容那样:

// DO NOT DO THIS!
if ([myArray isKindOfClass:[NSMutableArray class]])
{
    // Modify the object
}

现在给出一个不同用途的例子,假设我有一个NSObject实例,我想确定我是否有NSString或NSArray.

这两种类型都是类集群 - 但是从上面的文档中可以看出,危险在于isKindOfClass的答案:过于肯定(有时当你真的没有可变数组时回答"是")而是询问有关简单成员资格的问题.群集仍然有效.

一个例子:

NSObject *originalValue;

// originalValue gets set to some instance

if ( [originalValue isKindOfClass:[NSString class]] )
   // Do something with string

这个假设是否正确?使用isKindOfClass是否真的安全:针对类集群实例来确定成员身份?我对无所不在的NSString,NSArray和NSDictionary的答案特别感兴趣,但我有兴趣知道它是否可以推广.



1> drvdijk..:

文档中的警告使用了该NSMutableArray示例.假设某些开发人员将其NSMutableArray用作自己实现新类型数组的基类CoolFunctioningArray.这种CoolFunctioningArray设计不可变.然而,isKindOfClass将返回YESisKindOfClass:[NSMutableArray class],这是事实,但设计是没有的.

isKindOfClass:YES如果接收器某处继承自作为参数传递的类,则返回.isMemberOfClass:将返回YES只有接收方是否是作为参数而已,即不包括子传递的类的实例.

通常isKindOfClass:用于测试会员资格是不安全的.如果一个实例返回YESisKindOfClass:[NSString class],你只知道它会在定义的所有方法回应NSString类,但你不会肯定知道这些方法的实施可能会做.有人可能已经将子类NSString化为长度方法引发异常.

我认为你可以用isKindOfClass:这种测试,特别是如果你正在使用你自己的代码,你(作为一个好的撒玛利亚人)以这样的方式设计它将以我们所有人期望的方式响应(例如长度方法)一个NSString子类返回它所代表的字符串的长度,而不是引发异常).如果你正在使用许多奇怪的开发人员的外部库(比如CoolFunctioningArray应该被开发的开发人员),你应该isKindOfClass谨慎使用该方法,并且最好使用该isMemberOfClass:方法(可能多次测试一组的成员资格)类).


有人可以做这个子类 - 这打破了类应该工作的方式 - 但有人会_crazy_.如你所说.newacct的答案是这种情况下真正的"理由".
+1表示isKindOfClass和isMemberOfClass之间的区别。我碰巧也很需要这个答案。

2> 小智..:

让我们考虑以下代码:

if ([dict isKindOfClass:[NSMutableDictionary class]])
{
    [(NSMutableDictionary*)dict setObject:@1 forKey:@"1"];
}

我认为问题中引用的Apple注释的真正目的是这段代码很容易导致崩溃.这里的问题在于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);

实际上,在ALL 4变体中,dict的真正类别将是__NSCFDictionary.所有4种变体都将在第一个代码片段中通过测试.但是在2个案例中(NSDictionary和CFDictionaryRef声明),我们将使用类似的日志崩溃:

*由于未捕获的异常'NSInternalInconsistencyException'终止应用程序,原因:' - [__ NSCFDictionary setObject:forKey:]:发送到immutable对象的mutating方法'

所以,事情变得更加清晰.在2种情况下,我们可以修改对象,而在2种情况下则不允许.它取决于创建函数,并且可能由__NSCFDictionary对象本身跟踪可变性状态.

但是为什么我们为可变和不可变对象使用相同的类?可能的答案 - 由于C语言的限制(而CoreFoundation是一个C API,我们知道).我们在C有什么问题?可变和不可变字典类型的声明应反映以下内容:CFMutableDictionaryRef类型是CFDictionaryRef的子类型.但是C没有这方面的机制.但是我们希望能够在函数中传递CFMutableDictionary对象,期望CFDictionary对象,并且最好没有编译器发出恼人的警告.我们要做什么?

让我们看看以下CoreFoundation类型声明:

typedef const struct __CFDictionary * CFDictionaryRef;
typedef struct __CFDictionary * CFMutableDictionaryRef;

正如我们在这里看到的,CFDictionary和CFMutableDictionary由相同的类型表示,并且仅在const修饰符中有所不同.所以,这里开始麻烦.

这同样适用于NSArray,但情况稍微复杂一些.直接创建NSArray或NSMutableArray时,您将获得一些特殊类(分别为__NSArrayI和__NSArrayM).对于这个类崩溃没有转载.但是当你创建CFArray或CFMutableArray时,事情仍然是一样的.所以要小心!



3> newacct..:

你正在阅读警告错误.它只是因为它在内部被表示为可变的东西,并不意味着你应该试图改变它,因为改变它可能会违反你和你从阵列中得到的任何人之间的合同; 它可能违反了他们不会改变它的假设.

但是,给出的测试isKindOfClass:肯定仍然有效.如果[something isKindOfClass:[NSMutableArray class]],那么它是它的一个实例NSMutableArray或子类,这几乎意味着它是可变的.



4> Rob N..:

这是Apple文档中的一个奇怪的警告.这就像是说:"小心这个功能,因为如果你将它与其他破坏的代码一起使用,它将无法工作." 但你可以说任何事情.

如果设计遵循替换原则,它应该是,那么isKindOfClass将起作用.(维基百科:Liskov替代原则)

如果某些代码违反了这个原则,通过返回一个不响应addObject:和其他NSMutableArray方法的NSMutableArray子类的实例,那么该代码就有问题...除非它只是一个小规模的临时黑客......

推荐阅读
LEEstarmmmmm
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有