Cocoa:测试 NSString 是不可变还是可变?

发布于 2024-08-18 17:17:54 字数 483 浏览 7 评论 0原文

这会产生一个不可变的字符串对象:

NSString* myStringA = @"A";  //CORRECTED FROM: NSMutableString* myStringA = @"A";

这会产生一个可变的字符串对象:

NSMutableString* myStringB = [NSMutableString stringWithString:@"B"];

但是这两个对象都被报告为同一类型的对象,“NSCFString”:

NSLog(@"myStringA is type: %@, myStringB is type: %@", 
[myStringA class], [myStringB class]);

那么,这些对象在内部有何区别,以及如何测试这一点,以便我可以在对神秘字符串变量做坏事之前轻松确定它是不可变的还是可变的?

This produces an immutable string object:

NSString* myStringA = @"A";  //CORRECTED FROM: NSMutableString* myStringA = @"A";

This produces a mutable string object:

NSMutableString* myStringB = [NSMutableString stringWithString:@"B"];

But both objects are reported as the same kind of object, "NSCFString":

NSLog(@"myStringA is type: %@, myStringB is type: %@", 
[myStringA class], [myStringB class]);

So what is distinguishing these objects internally, and how do I test for that, so that I can easily determine if a mystery string variable is immutable or mutable before doing something evil to it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

叹沉浮 2024-08-25 17:17:54

这些文档包含相当长的解释,说明为什么 Apple 不希望您这样做以及为什么他们明确不支持它 接收可变对象。总结是:

所以不要对对象做出决定
基于什么内省的可变性
告诉你一个物体。对待
对象是可变的或不基于
API 向您提供的内容
边界(即基于
返回类型)。如果您需要
将一个对象明确标记为
当你传递它时可变或不可变
将该信息作为
标记与对象一起。

我发现他们的 NSView 示例最容易理解,它说明了一个基本的 Cocoa 问题。您有一个名为“elements”的 NSMutableArray,您希望将其公开为数组,但不希望调用者弄乱。您有多种选择:

  1. 将 NSMutableArray 公开为 NSArray。
  2. 始终在请求时制作不可变的副本
  3. 将元素存储为 NSArray 并在每次发生变化时创建一个新数组。

我已经在不同的时间点完成了所有这些工作。 #1 是迄今为止最简单、最快的解决方案。这也很危险,因为数组可能会在调用者背后发生变异。但苹果表示这就是他们在某些情况下所做的(请注意 -subviews 在 NSView 中)。我可以确认,虽然 #2 和 #3 更安全,但它们可能会造成重大性能问题,这可能就是 Apple 选择不在经常访问的成员(例如 -subviews)上使用它们的原因。

所有这一切的结果是,如果你使用#1,那么内省会误导你。你有一个 NSMutableArray 转换为 NSArray,内省将表明它是可变的(内省无法知道其他情况)。但你不能改变它。只有编译时类型检查才能告诉您这一点,因此这是您唯一可以信任的事情。

解决这个问题的方法是使用可变数据结构的某种快速写时复制不可变版本。这样#2 就可能以不错的性能完成。我可以想象对 NSArray 集群的更改将允许这种情况,但它在今天的 Cocoa 中不存在(并且可能会影响正常情况下的 NSArray 性能,使其无法启动)。即使我们拥有它,也可能有太多依赖于当前行为的代码,以至于无法信任可变性内省。

The docs include a fairly long explanation on why Apple doesn't want you to do this and why they explicitly do not support it in Receiving Mutable Objects. The summary is:

So don’t make a decision on object
mutability based on what introspection
tells you about an object. Treat
objects as mutable or not based on
what you are handed at the API
boundaries (that is, based on the
return type). If you need to
unambiguously mark an object as
mutable or immutable when you pass it
to clients, pass that information as a
flag along with the object.

I find their NSView example the easiest to understand, and it illustrates a basic Cocoa problem. You have an NSMutableArray called "elements" that you want to expose as an array, but don't want callers to mess with. You have several options:

  1. Expose your NSMutableArray as an NSArray.
  2. Always make a non-mutable copy when requested
  3. Store elements as an NSArray and create a new array every time it mutates.

I've done all of these at various points. #1 is by far the simplest and fastest solution. It's also dangerous, since the array might mutate behind the caller's back. But Apple indicates it's what they do in some cases (note the warning for -subviews in NSView). I can confirm that while #2 and #3 are much safer, they can create major performance problems, which is probably why Apple has chosen not to use them on oft-accessed members like -subviews.

The upshot of all of this is that if you use #1, then introspection will mislead you. You have an NSMutableArray cast as an NSArray, and introspection will indicate that it's mutable (introspection has no way to know otherwise). But you must not mutate it. Only the compile-time type check can tell you that, and so it's the only thing you can trust.

The fix for this would be some kind of fast copy-on-write immutable version of a mutable data structure. That way #2 could possibly be done with decent performance. I can imagine changes to the NSArray cluster that would allow this, but it doesn't exist in Cocoa today (and could impact NSArray performance in the normal case, making it a non-starter). Even if we had it, there's probably too much code out there that relies on the current behavior to ever allow mutability introspection to be trusted.

忆依然 2024-08-25 17:17:54

没有(已记录的)方法来确定字符串在运行时是否可变。

您可能期望以下其中一项可以工作,但它们都不起作用:

[[s class] isKindOfClass:[NSMutableString class]]; // always returns false
[s isMemberOfClass:[NSMutableString class]]; // always returns false
[s respondsToSelector:@selector(appendString)]; // always returns true

此处提供更多信息,尽管它不能帮助您解决问题:

http://www.cocoabuilder.com/archive/cocoa/111173-mutability.html

There's no (documented) way to determine if a string is mutable at runtime or not.

You would expect one of the following would work, but none of them work:

[[s class] isKindOfClass:[NSMutableString class]]; // always returns false
[s isMemberOfClass:[NSMutableString class]]; // always returns false
[s respondsToSelector:@selector(appendString)]; // always returns true

More info here, although it doesn't help you with the problem:

http://www.cocoabuilder.com/archive/cocoa/111173-mutability.html

纵性 2024-08-25 17:17:54

如果您想检查调试目的,以下代码应该可以工作。不可变对象的复制是其本身,而它是可变类型的真正复制,这就是代码所基于的。请注意,由于它调用 copy 它很慢,但对于调试来说应该没问题。如果您想检查除调试之外的任何其他原因,请参阅 Rob 答案(然后忘记它)。

BOOL isMutable(id object)
{
   id copy = [object copy];
   BOOL copyIsADifferentObject = (copy != object);
   [copy release];
   return copyIsADifferentObject;
}

免责声明:当然,不能保证对于不可变类型,复制与保留等效。您可以确定,如果 isMutable 返回 NO,则它不是可变的,因此该函数应该命名为 canBeMutable。然而,在现实世界中,一个非常安全的假设是不可变类型(NSString,NSArray)将实现这种优化。有很多代码,包括像 NSDictionary 这样需要从不可变类型快速复制的基本内容。

If you want to check for debugging purposes the following code should work. Copy on immutable object is itself, while it's a true copy for mutable types, that's what the code is based on. Note that since it's calling copy it's slow, but should be fine for debugging. If you'd like to check for any other reasons than debugging see Rob answer (and forget about it).

BOOL isMutable(id object)
{
   id copy = [object copy];
   BOOL copyIsADifferentObject = (copy != object);
   [copy release];
   return copyIsADifferentObject;
}

Disclaimer: of course there is no guarantee that copy is equivalent with retain for immutable types. You can be sure that if isMutable returns NO then it's not mutable so the function should be probably named canBeMutable. In the real world however, it's a pretty safe assumption that immutable types (NSString,NSArray) will implement this optimization. There is a lot of code out including basic things like NSDictionary that expects fast copy from immutable types.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文