我有两个新问题:
1)考虑这一行:
NSString *myString = [[NSString alloc] initWithString: @"Value"];
我学到了两件事,但我想确认一下:据我所知,"alloc"消息表明NSString的实例将存储在"堆"内存中。我还了解到,诸如"字符"之类的原始变量存储在"堆栈"内存中。
这是否意味着:
- NSString的实例存储在堆内存中;
- 并且这个对象有一个iVar指针(当initWithString方法被调用时)到原始"字符"的"值"字符串,它驻留在堆栈内存中?这在现实中是如何运作的呢?
第二个问题直接相关,并使我个人陷入困境(可能是因为我遗漏了一点):2)你会参考哪一种方法,为什么?:
NSString *myString = [[NSString alloc] initWithString: @"Value"];
NSString *myString = @"Value";
如果我的第一个问题得到确认,这两种方法都应该"最终"指向存储在堆栈内存中的字符。因此,我实际上不明白使用第一个选项的目的,并为保留计数而烦恼。
简短的回答:在这种情况下,两行都有相同的结果,可以将字符串常量直接分配给myString
。
长答:
Objective-C对象确实是在堆上分配的。然而,"原始"值并不总是存储在堆栈中。局部变量存储在堆栈中,无论它们是否是原始变量。(例如,您可以声明一个局部变量,它是一个结构体,它不被认为是原始的。)您可以在堆上存储原始值,但在这种情况下访问它们的唯一方法是通过指针。例如:
int *someInt = malloc(sizeof(int)); // allocate a block of memory to hold an int
*someInt = 42; // store data in that memory
总是使用指针来引用Objective-C对象的原因是对象总是在堆上分配的。如果你想在堆栈上存储一些东西,编译器需要知道它的大小。在Objective-C中,对象的大小直到程序实际运行时才知道。
回到字符串。尝试执行以下两行:
NSString *foo = [[NSString alloc] initWithString:@"foo"];
NSString *bar = @"foo";
如果你在第二行之后换行,你会发现foo
和bar
包含相同的地址;也就是说,它们指向同一个对象。因为NSString对象是不可变的,创建一个带有常量字符串的对象只会返回一个指向该常量的指针。
为什么在NSString中有-initWithString:
如果那是它的全部功能?NSString是一个"类簇"也就是说NSString是几个不同内部类的公共接口。如果你传递一个NSString*它不是一个常量到-initWithString:
,你得到的对象可能是一个不同类的实例而不是你使用常量时得到的。作为一个类集群,NSString向你隐藏了很多实现细节,这样你就可以获得不同类型字符串的良好性能,而不必担心它是如何工作的。
NSString
s很有趣,因为直到最近它们还是唯一一种可以作为文字提供的Objective-C对象类型。据我所知,iOS 4和OS X 10.6中新增的block对象也是最近新增的,但它们有自己的特殊规则,所以我只是为了完整起见才提出来。
C原语可以存储在堆或堆栈中。例如:
- (void)someMethod
{
int i = 3; // i is on the stack
}
- (void)someOtherMethod
{
int *i = (int *)malloc(sizeof(int)); // i now points to an 'int' on the heap
}
大多数Objective-C对象只能存储在堆上。你说alloc
在堆上提供一个对象的新副本是非常正确的,而你调用myString
的结果将是在堆上有一个指向NSString的指针。
然而,@""
语法是创建对象的简写。@"Value"
实际上创建了一个对象字面量。例如,你可以这样写:
NSLog(@"%@", [@"Value" substringFromIndex:1]);
输出将是' value '。您可以将substringFromIndex:
消息发送给@"Value"
,因为它是一个文字对象。
确切地说,NSString
对initWithString:
所做的是一个具体的实现,但你可以肯定的是,如果需要的话,它将获取指向的东西的副本。否则你会看到奇怪的行为,如果你这样做:
NSMutableString *mutableString = [NSMutableString stringWithString:@"String"];
NSString *immutableString = [NSString stringWithString:mutableString];
[mutableString appendString:@" + hat"];
// immutableString would now have mutated if it was simply
// keeping a reference to the string passed in
因此,您不必担心传递给它的任何东西的生命周期。这是NSString
的工作。
在实践中,NSString
对象字面量实际上永远不会过期,所以这一点有点没有意义。然而,如果你使用initWithUTF8String:
或其他接受C文字的东西,并且该C文字在堆栈上,你仍然没有什么可担心的,因为NSString
会处理它。
回答你的第二个问题,我倾向于第二个版本,因为它更短,因此更清楚地表明你打算做什么。从理论上讲,后者对性能有一些好处——特别是如果您在多个地方使用相同的文字——但它们是如此令人难以置信地微不足道,以至于现在不值得考虑。