我正在使用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:这里发生了什么,我错过了?
答案 0 :(得分:10)
简短回答:该字符串的类型为NSCFString
,这是一个继承自NSMutableString
的类,因此它响应NSMutableString
中声明的方法的选择器,包括超类。
答案不是那么简短:基础字符串是使用Core Foundation字符串免费桥接的。开发人员使用不透明类型CFStringRef
(桥接NSString
)和CFMutableStringRef
(桥接NSMutableString
)来引用这些字符串,因此,乍一看,有两种不同的类型字符串:不可变和可变。
从Core Foundation内部实现的角度来看,有一个名为struct __CFString
的私有类型。此私有类型保留一个位字段,除其他信息外,还存储字符串是可变的还是不可变的。具有单一类型简化了实现,因为许多函数由不可变和可变字符串共享。
每当调用对可变字符串进行操作的Core Foundation函数时,它首先读取该字段并检查该字符串是可变的还是不可变的。如果参数应该是一个可变的字符串,但事实上它不是,则该函数返回错误(例如_CFStringErrNotMutable
)或失败一个断言(例如__CFAssertIsStringAndMutable(cf)
)。
无论如何,这些都是实施细节,它们将来可能会发生变化。 NSString
未声明-appendString:
这一事实并不意味着每个NSString
实例都不响应相应的选择器 - 请substitutability。同样的情况适用于其他可变/不可变类,例如NSArray
和NSMutableArray
。从开发人员的角度来看,重要的是返回的对象是与返回类型匹配的类型 - 它可以是类型本身或该类型的任何子类型。类集群使这一点更复杂,但情况本身并不局限于类集群。
总之,您只能期望一个方法返回一个对象,该对象的类型属于返回值类型的层次结构(即,类型本身或子类型)。不幸的是,这意味着您无法检查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
我认为从实现的角度来看这很方便 - NSString
和NSMutableString
都可以由一个公共类支持(=更少的代码重复),这个类确保你不违反不变性:
// “Attempt to mutate immutable object with appendString:”
[[NSString string] appendString:@"foo"];
在这个答案中有很多猜测工作,我真的不明白这些东西,让我们希望有人知道的更好。
答案 2 :(得分:1)
您不应该假设不的方法。该方法可以在内部使用,也可以出于任何原因使用。从技术上讲,它只是私有API。
您只与公共声明( docs )签订合同,并且他们不会显示该消息。因此,如果您使用其他功能,请准备好相当快地陷入麻烦。