objective-c 对 NSString 实例使用 isKindOfClass: 来确定类型是否安全?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1096772/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is it safe to use isKindOfClass: against an NSString instance to determine type?
提问by Kendall Helmstetter Gelner
From the isKindOfClass: method documentation in NSObject:
来自NSObject 中的isKindOfClass: 方法文档:
Be careful when using this method on objects represented by a class cluster. Because of the nature of class clusters, the object you get back may not always be the type you expected.
在由类簇表示的对象上使用此方法时要小心。由于类簇的性质,您返回的对象可能并不总是您期望的类型。
The documentation then proceeds to give an example of why you should never ask something like the following of an NSArray instance:
然后文档继续给出一个例子,说明为什么你永远不应该询问 NSArray 实例的以下内容:
// DO NOT DO THIS!
if ([myArray isKindOfClass:[NSMutableArray class]])
{
// Modify the object
}
Now to give an example of a different use, let's say I have an instance of NSObject where I would like to determine if I have an NSString or NSArray.
现在举一个不同用途的例子,假设我有一个 NSObject 的实例,我想确定我是否有一个 NSString 或 NSArray。
Both of these types are class clusters - but it seems from the documentation above that the danger lies in the answer to isKindOfClass: being too affirmative (answering YES sometimes when you really do not have a mutable array) whereas asking a question about simple membership in a cluster would still be valid.
这两种类型都是类簇 - 但从上面的文档看来,危险在于 isKindOfClass 的答案:过于肯定(有时当你真的没有可变数组时回答是),而问一个关于简单成员的问题集群仍然有效。
An example:
一个例子:
NSObject *originalValue;
// originalValue gets set to some instance
if ( [originalValue isKindOfClass:[NSString class]] )
// Do something with string
Is this assumption correct? Is it really safe to use isKindOfClass: against class cluster instances to determine membership? I'm specifically interested in the answer for the omnipresent NSString, NSArray and NSDictionary but I'd be interested to know if it's generalizable.
这个假设正确吗?使用 isKindOfClass: 针对类集群实例来确定成员资格真的安全吗?我对无处不在的 NSString、NSArray 和 NSDictionary 的答案特别感兴趣,但我很想知道它是否可以推广。
采纳答案by drvdijk
The warning in the documentation uses the NSMutableArrayexample. Suppose some developer uses NSMutableArrayas base class for his own implementation of a new kind of array CoolFunctioningArray. This CoolFunctioningArrayby design is not mutable. However, isKindOfClasswill return YESto isKindOfClass:[NSMutableArray class], which is true, but by design is not.
文档中的警告使用了NSMutableArray示例。假设某些开发人员将其NSMutableArray用作他自己实现的一种新型数组的基类CoolFunctioningArray。这种CoolFunctioningArray设计是不可改变的。但是,isKindOfClass将返回YES到isKindOfClass:[NSMutableArray class],这是真的,但按设计不是。
isKindOfClass:will return YESif the receiver somewhere inherits from the class passed as argument. isMemberOfClass:will return YESonly if the receiver is an instance of the class passed as argument only, i.e. not including subclasses.
isKindOfClass:YES如果某处的接收者从作为参数传递的类继承,则将返回。仅当接收者是仅作为参数传递的类的实例时isMemberOfClass:才会返回YES,即不包括子类。
Generally isKindOfClass:is not safe to use to test for membership. If an instance returns YESfor isKindOfClass:[NSString class], you know only that it will respond to all methods defined in the NSStringclass, but you will not know for sure what the implementation of those methods might do. Someone might have subclassed NSStringto raise an exception for the length method.
通常isKindOfClass:用于测试会员资格是不安全的。如果实例返回YESfor isKindOfClass:[NSString class],您只知道它将响应NSString类中定义的所有方法,但您不确定这些方法的实现可能会做什么。有人可能已经子类化NSString以引发 length 方法的异常。
I think you could use isKindOfClass:for this kind of testing, especially if you're working with your own code which you (as a good Samaritan) designed in such a way it will respond in a way we all expect it to (e.g. the length method of an NSStringsubclass to return the length of the string it represents instead of raising an exception). If you're using a lot of external libraries of weird developers (such as the developer of the CoolFunctioningArray, which should be shot) you should use the isKindOfClassmethod with caution, and preferably use the isMemberOfClass:method (possibly multiple times to test membership of a group of classes).
我认为您可以isKindOfClass:用于这种测试,特别是如果您使用自己的代码(作为一个好撒玛利亚人)以这样一种方式设计它会以我们都期望的方式响应(例如长度方法)的NSString子类返回它代表的字符串的长度而不是引发异常)。如果你使用了很多奇怪的开发者的外部库(比如 的开发者CoolFunctioningArray,应该被枪毙)你应该isKindOfClass谨慎使用该方法,最好使用该isMemberOfClass:方法(可能多次测试一组成员的成员资格)类)。
回答by Granfalloner
Lets consider the following code:
让我们考虑以下代码:
if ([dict isKindOfClass:[NSMutableDictionary class]])
{
[(NSMutableDictionary*)dict setObject:@1 forKey:@"1"];
}
I think the true purpose of the Apple's note quoted in the question is that this code could easily lead to crash. And the problem here lies in toll-free bridging with CoreFoundation. Lets consider in more details 4 variants:
我认为问题中引用的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);
Indeed, in ALL 4 variants the true class of the dict will be __NSCFDictionary. And all 4 variants will pass the test in the first code snippet. But in 2 cases (NSDictionary and CFDictionaryRef declarations) we will crash with log something like that:
实际上,在所有 4 个变体中,dict 的真正类将是 __NSCFDictionary。并且所有 4 个变体都将通过第一个代码片段中的测试。但是在两种情况下(NSDictionary 和 CFDictionaryRef 声明),我们将崩溃,日志如下:
*Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFDictionary setObject:forKey:]: mutating method sent to immutable object'
*由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“-[__NSCFDictionary setObject:forKey:]: mutating method sent to immutable object”
So, things getting a little bit clearer. In 2 cases we are allowed to modify object, and in 2 cases are not. It depends on creation function, and probably mutability state is tracked by __NSCFDictionary object itself.
所以,事情变得有点清楚了。在 2 种情况下我们可以修改对象,在 2 种情况下不允许。它取决于创建函数,并且可能可变状态由 __NSCFDictionary 对象本身跟踪。
But why do we use the same class for mutable and immutable objects? Possible answer - because of C language limitations (and CoreFoundation is a C API, as we know). What problem do we have in C? The declarations of mutable and immutable dictionary types should reflect the following: CFMutableDictionaryRef type is a subtype of CFDictionaryRef. But C has no mechanisms for this. But we want to be able to pass CFMutableDictionary object in function expecting CFDictionary object, and preferably without compiler emitting annoying warnings. What we have to do?
但是为什么我们对可变和不可变对象使用相同的类呢?可能的答案 - 由于 C 语言的限制(正如我们所知,CoreFoundation 是一个 C API)。我们在 C 中有什么问题?可变和不可变字典类型的声明应反映以下内容: CFMutableDictionaryRef 类型是 CFDictionaryRef 的子类型。但是 C 没有这方面的机制。但是我们希望能够在期望 CFDictionary 对象的函数中传递 CFMutableDictionary 对象,并且最好不要编译器发出烦人的警告。我们必须做什么?
Lets have a look at the following CoreFoundation type declarations:
让我们看看以下 CoreFoundation 类型声明:
typedef const struct __CFDictionary * CFDictionaryRef;
typedef struct __CFDictionary * CFMutableDictionaryRef;
As we can see here, CFDictionary and CFMutableDictionary are represented by the same type, and differ only in the const modifier. So, here start troubles.
正如我们在这里看到的,CFDictionary 和 CFMutableDictionary 由相同的类型表示,仅在 const 修饰符上有所不同。所以,这里开始麻烦。
The same generally applies to NSArray too, but the situation is a little bit more complicated. When you create NSArray or NSMutableArray directly, you will get some special classes (__NSArrayI and __NSArrayM respectively). For this classes crash in not reproduced. But when you create CFArray or CFMutableArray, the things remain the same. So be careful!
这同样适用于 NSArray,但情况稍微复杂一些。当你直接创建 NSArray 或 NSMutableArray 时,你会得到一些特殊的类(分别是 __NSArrayI 和 __NSArrayM)。对于这个类崩溃没有重现。但是当您创建 CFArray 或 CFMutableArray 时,情况保持不变。所以要小心!
回答by newacct
You are reading the warning wrong. All it says is that, just because it is internally represented as something that is mutable, does not mean you should try to mutate it, because mutating it could violate the contract between you and whomever you got the array from; it might violate their assumptions that you will not mutate it.
你读错了警告。它只是说,仅仅因为它在内部表示为可变的东西,并不意味着你应该尝试改变它,因为改变它可能违反你和你从谁那里得到数组的人之间的契约;它可能会违反他们的假设,即您不会对其进行变异。
However, the test given by isKindOfClass:is definitely still valid. If [something isKindOfClass:[NSMutableArray class]], then it is an instance of NSMutableArrayor subclass thereof, which pretty much means it's mutable.
但是,由 给出的测试isKindOfClass:肯定仍然有效。如果[something isKindOfClass:[NSMutableArray class]],则它是其实例NSMutableArray或其子类,这几乎意味着它是可变的。
回答by Rob N
This is a strange warning in the Apple docs. It's like saying: "be careful of this function, because if you use it with other broken code, it won't work." But you can say that with anything.
这是 Apple 文档中的一个奇怪警告。就像在说:“小心这个函数,因为如果你把它和其他损坏的代码一起使用,它就不会工作。” 但是你可以说任何事情。
If the design follows the substitution principle, which it should, then isKindOfClass will work. (wikipedia: Liskov Substitution Principle)
如果设计遵循替代原则,那么 isKindOfClass 将起作用。(维基百科:Liskov 替换原则)
If some code violates the principle, by returning an instance of an NSMutableArray subclass that doesn't respond to the addObject: and other NSMutableArray methods, then that code has a problem... unless it's just a small-scale temporary hack...
如果某些代码违反了原则,通过返回不响应 addObject: 和其他 NSMutableArray 方法的 NSMutableArray 子类的实例,那么该代码就有问题......除非它只是一个小规模的临时黑客......

