7

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

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]);

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

4

3 回答 3

13

这些文档包含一个相当长的解释,说明为什么 Apple 不希望您这样做,以及为什么他们在Receiving Mutable Objects中明确不支持它。总结是:

因此,不要根据自省告诉您有关对象的内容来决定对象的可变性。根据您在 API 边界处理的内容(即基于返回类型)将对象视为可变或不可变。如果您需要在将对象传递给客户端时明确地将其标记为可变或不可变,请将该信息作为标志与对象一起传递。

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

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

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

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

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

于 2010-01-19T15:48:25.640 回答
4

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

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

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

于 2010-01-19T12:37:59.437 回答
4

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

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

[[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

于 2010-01-19T10:12:10.173 回答