我知道苹果公司警告不喜欢使用它。但考虑到他们的推理,结果与预期相去甚远。
下面是我的调试输出--结果在代码中没有什么不同--以下只是为了简洁起见:
(lldb) po [@"Hello" isKindOfClass:[NSMutableString class]]
true => A mutable string?
(lldb) po [[@"Hello" mutableCopy] isKindOfClass:[NSMutableString class]]
0x00000001019f3201 => What's that?
(lldb) po [[@"Hello" mutableCopy] isMemberOfClass:[NSMutableString class]]
0x000000010214e400 => What's that?
(lldb) po [@"Hello" isMemberOfClass:[NSMutableString class]]
false => Once again?此外,我删除了所有字符串文字代码,并测试了以下内容:
NSMutableString * m = [[NSMutableString alloc] initWithString:@"Hello"];
bool b = [m isKindOfClass:[NSMutableString class]];
NSLog(@"%d", b); --> 1 Expected.
b = [m isKindOfClass:[NSString class]];
NSLog(@"%d", b); --> 1 Expected.
b = [m isMemberOfClass:[NSString class]];
NSLog(@"%d", b); --> 0 Expected.
b = [m isMemberOfClass:[NSMutableString class]];
NSLog(@"%d", b); --> 0 Not Expected.有开悟吗?
更新:
苹果自己的观点:
在类集群表示的对象上使用此方法时要小心。由于类集群的性质,返回的对象可能并不总是您所期望的类型。如果您调用一个返回类集群的方法,则该方法返回的确切类型是您可以对该对象做什么的最佳指示。
为什么不简单地说不要在集群类中使用isKindOfClass和isMemberOfClass?
该解释禁止从以下角度使用:
你可能最终会修改一些你不该做的事情。
而不是说:
这些方法不适用于类集群。(在示例中,我已经展示了-我显然传递了正确的对象,但仍然没有得到预期的结果。)
更新2:
向苹果雷达提交。
发布于 2016-01-01 23:58:35
这些方法不会像你在评论中所说的那样“误导”。因为NSString和NSMutableString是类集群,所以它们可以返回任何具体子类的实例,即NSString或NSMutableString。
碰巧,NSString集群中的大多数具体子类也是NSMutableString的子类。他们没有使用实际的类来控制可变性,而是使用一个标志或类似的东西。所有的完全有效和符合设计合同。
这就是为什么[@"Hello" isKindOfClass:[NSMutableString class]]返回true的原因。你问的是“可变的字符串?”不是的。这个表达式不是对可变性的有效测试。如文档所示,不存在有效的可变性测试。这是你误解的核心。您不能试图询问对象的类以确定它是否是可变的。您必须尊重API中指针的静态类型。
编辑:--这在目标C编程中的概念:对象可变接收可变对象中有记录
使用返回类型,而不是内省 要确定是否可以更改接收到的对象,消息的接收方必须依赖于返回值的形式类型。例如,如果接收到类型为不可变的数组对象,则不应尝试对其进行变异。根据对象的类成员身份来确定对象是否是可变的是不可接受的编程实践,例如: 如果( [anArray是isKindOfClass:NSMutableArray类]){ // add,从anArray }中删除对象 由于与实现相关的原因,在这种情况下
isKindOfClass:返回的内容可能不准确。但是,出于其他原因,您不应该根据类成员资格来假设对象是否是可变的。您的决定应该完全取决于自动出售对象的方法的签名对其可更改性的影响。如果您不确定一个对象是可变的还是不可变的,那么假设它是不可变的。 几个例子可能有助于澄清为什么这条准则是重要的:
NSView请求它的子视图(使用subviews方法),它返回一个被声明为NSArray但内部可能是NSMutableArray的对象。然后,将数组传递给其他代码,这些代码通过内省将其确定为可变的,并对其进行更改。通过更改这个数组,代码将改变NSView类的内部数据结构。所以,不要根据内省告诉你的关于一个对象的内容来假设对象的易变性。根据API边界传递的内容(即基于返回类型),将对象视为可变的或不可更改的。如果在将对象传递给客户端时,需要明确地将其标记为可变或不可变,则将该信息与对象一起传递为标志。
正如其他人所提到的,-isMemberOfClass:测试是该精确类的实例,而不是任何子类的实例。对于类集群,它总是返回false,因为公共类是抽象的,永远不会有实例。
其他奇怪的结果可能是因为非对象值使用了po (“打印对象”的缩写)。对布尔表达式使用p命令。
发布于 2016-01-03 19:07:05
不要在代码中使用内省来确定易变性或改变行为(在非常有限的情况之外)。请使用静态类型和强定义的数据结构(包括强定义的plist结构)。
Objective运行时提供的内省功能(其中NSObject上的内省方法是针对它实现的)既不具有误导性,也不会返回不正确的结果。
它们揭示了各种对象是如何实现的具体细节。这可能与头文件中声明的内容有很大不同。
或者,换句话说:编译时与运行时可能有很大的不同。
这既是正确的,也是可以的。而且令人困惑。
Objective实现了鸭子类型的强大概念.也就是说,如果您引用某个声明为NSString*的东西,那么不管真正指向什么引用,只要它响应为类NSString声明的API契约。
这种困惑来自于试图把目标C作为一种完全动态的、内省驱动的语言。它不是为此而设计的(嗯,它是这样的,但是这个概念在1990年左右就被抛弃了),而且,随着时间的推移,强大的打字越来越成为常态。也就是说,让编译器找出某件东西是否有效,并且不要在运行时试图再次猜测。
https://stackoverflow.com/questions/34561101
复制相似问题