2011-09-11 57 views
8

一个很好的方式,我有2个新的问题:被存储在堆或堆栈上NSString的,什么是初始化一个

1)考虑这一行:

NSString *myString = [[NSString alloc] initWithString: @"Value"]; 

有两件事情我了解,但我想确认: 据我所知,“alloc”消息表明NSString的实例将存储在“堆”内存中。 我也理解像“chars”这样的原始变量存储在“堆栈”内存中。

这是否意味着:

  • 的NSString的实例存储在堆存储器;
  • 而且这个对象有一个iVar指针(当initWithString方法被调用时)到原始“chars”的“Value”字符串,该字符串驻留在堆栈内存中? 这是如何工作的现实?

第二个问题是直接相关的,并导致了我个人的困境(也许是因为我缺少一个点): 2)你去看这两种方法的,为什么?:

NSString *myString = [[NSString alloc] initWithString: @"Value"]; 
NSString *myString = @"Value"; 

如果我的第一个问题得到证实,两种方法都应该“最终”指向存储在堆栈内存中的字符。因此,我实际上并没有看到使用第一个选项的目的,并且受到保留计数的困扰。

回答

13

简短回答:在这种情况下,两条线都具有相同的结果可以直接将字符串常量分配给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"; 

如果第二线突破之后,你会发现foobar包含相同的地址;也就是说,他们指向同一个对象。因为NSString对象是不可变的,所以用常量字符串创建一个就返回一个指向该常量的指针。

为什么在NSString中甚至有一个-initWithString:如果只是这样呢? NSString是一个“类集群”,也就是说NSString是几个不同内部类的公共接口。如果你将一个不是常量的NSString *传递给-initWithString:,那么你返回的对象可能是一个不同于你在使用常量时得到的类的实例。作为类集群,NSString隐藏了大量的实现细节,这样您就可以获得不同类型字符串的良好性能,而不必担心它是如何工作的。

4

NSString s很有趣,因为它们直到最近才是唯一可以作为文字提供的Objective-C对象。在iOS 4和OS X 10.6中添加的块对象是我所知道的另一个最近添加的内容,但它们有其自己的特定规则,所以我只是为了完整而提及。

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

并且输出将是'alue'。您可以将substringFromIndex:消息发送到@"Value",因为它是一个文字对象。

NSStringinitWithString:的具体做法是具体实现,但是您可以确定,如果需要,它会将指向的内容复制一份。否则,你会看到奇怪的行为,如果你做了这样的事情:

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会处理它。

在回答你的第二个问题时,我倾向于第二个版本,理由是它更短,因此更清楚地表明你打算做什么。后者有一些理论上的性能好处 - 特别是如果你在多个地方使用相同的文字 - 但它们如此令人难以置信的微不足道,因为现在不值得考虑。