为什么NSString响应appendString?

时间:2011-05-05 08:43:59

标签: objective-c nsstring

我正在使用MacOS-X 10.6.7和Xcode 4.0.2上的Objective-C中的respondsToSelector方法来识别对象是否会响应某些消息。根据手册,NSString不应该响应appendString:而NSMutableString应该。这是测试它的代码片段:

int main (int argc, const char * argv[])
{

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    NSString *myString = [[NSString alloc] init];

    if ([myString respondsToSelector:@selector(appendString:)]) {
        NSLog(@"myString responds to appendString:");
    } else {
        NSLog(@"myString doesn't respond to appendString:");
    }

    // do stuff with myString

    [myString release];
    [pool drain];
    return 0;
}

这是输出:

Class02[10241:903] myString responds to appendString:

我有点期待相反的事情。 NSString对象如何响应appendString:这里发生了什么,我错过了?

3 个答案:

答案 0 :(得分:10)

简短回答:该字符串的类型为NSCFString,这是一个继承自NSMutableString的类,因此它响应NSMutableString中声明的方法的选择器,包括超类。

答案不是那么简短:基础字符串是使用Core Foundation字符串免费桥接的。开发人员使用不透明类型CFStringRef(桥接NSString)和CFMutableStringRef(桥接NSMutableString)来引用这些字符串,因此,乍一看,有两种不同的类型字符串:不可变和可变。

从Core Foundation内部实现的角度来看,有一个名为struct __CFString的私有类型。此私有类型保留一个位字段,除其他信息外,还存储字符串是可变的还是不可变的。具有单一类型简化了实现,因为许多函数由不可变和可变字符串共享。

每当调用对可变字符串进行操作的Core Foundation函数时,它首先读取该字段并检查该字符串是可变的还是不可变的。如果参数应该是一个可变的字符串,但事实上它不是,则该函数返回错误(例如_CFStringErrNotMutable)或失败一个断言(例如__CFAssertIsStringAndMutable(cf))。

无论如何,这些都是实施细节,它们将来可能会发生变化。 NSString未声明-appendString:这一事实并不意味着每个NSString实例都不响应相应的选择器 - 请substitutability。同样的情况适用于其他可变/不可变类,例如NSArrayNSMutableArray。从开发人员的角度来看,重要的是返回的对象是与返回类型匹配的类型 - 它可以是类型本身或该类型的任何子类型。类集群使这一点更复杂,但情况本身并不局限于类集群。

总之,您只能期望一个方法返回一个对象,该对象的类型属于返回值类型的层次结构(即,类型本身或子类型)。不幸的是,这意味着您无法检查Foundation对象是否可变。但话又说回来,你真的需要这个检查吗?


您可以使用CFShowStr()函数从字符串中获取信息。在您问题的示例中,添加

CFShowStr((CFStringRef)myString);

您应该得到类似于:

的输出
Length 0
IsEightBit 1
HasLengthByte 0
HasNullByte 1
InlineContents 0
Allocator SystemDefault
Mutable 0
Contents 0x0

,其中

Mutable 0

表示字符串实际上是不可变的。

答案 1 :(得分:2)

这可能与实施有关。 NSString是一个类集群,这意味着NSString只是一个公共接口,实际的实现类是不同的(see what the class message gives you)。

同时NSString也免费桥接到CFString,这意味着您可以通过强制转换在这两种类型之前自由切换:

NSString *one = @"foo";
CFStringRef two = (CFStringRef)one; // valid cast

当你创建一个新的字符串时,你真的得到一个NSCFString,一个围绕CFString的薄包装。重点是,当您创建一个新的可变字符串时,您还会获得NSCFString的实例。

Class one = [[NSString string] class]; // NSCFString
Class two = [[NSMutableString string] class]; // NSCFString

我认为从实现的角度来看这很方便 - NSStringNSMutableString都可以由一个公共类支持(=更少的代码重复),这个类确保你不违反不变性:

// “Attempt to mutate immutable object with appendString:”
[[NSString string] appendString:@"foo"];

在这个答案中有很多猜测工作,我真的不明白这些东西,让我们希望有人知道的更好。

答案 2 :(得分:1)

您不应该假设的方法。该方法可以在内部使用,也可以出于任何原因使用。从技术上讲,它只是私有API。

您只与公共声明( docs )签订合同,并且他们不会显示该消息。因此,如果您使用其他功能,请准备好相当快地陷入麻烦。