NSString 是存储在堆上还是堆栈上?初始化 NSString 的好方法是什么?
我有 2 个新问题:
1)考虑这一行:
NSString *myString = [[NSString alloc] initWithString: @"Value"];
我学到了两件事,但我想确认一下: 据我所知,“alloc”消息表明 NSString 的实例将存储在“堆”内存中。 我还了解诸如“字符”之类的原始变量存储在“堆栈”内存中。
这是否意味着:
- NSString的实例存储在堆内存中;
- 并且该对象有一个 iVar 指针(当调用 initWithString 方法时)指向驻留在堆栈内存中的原始“chars”的“Value”字符串? 这实际上是如何运作的?
第二个问题是直接相关的,给我带来了个人的困境(可能是因为我遗漏了一点): 2)您会参考这两种方法中的哪一种?为什么?:
NSString *myString = [[NSString alloc] initWithString: @"Value"];
NSString *myString = @"Value";
如果我的第一个问题得到证实,两种方法都应该“最终”指向存储在堆栈内存中的字符。因此,我实际上并没有看到使用第一个选项并为保留计数所困扰的目的。
I have 2 new questions:
1) Consider this line:
NSString *myString = [[NSString alloc] initWithString: @"Value"];
There were two things I learned, but I would like confirmation:
As I learned, the "alloc" message indicates that the instance of NSString will be stored in the "heap" memory.
I understood also that primitive variables such as "chars" are stored in the "stack" memory.
Does this mean that:
- the instance of NSString is stored in the heap memory;
- AND that this object has an iVar pointer (when the initWithString method was called) to the "Value" string of primitive "chars", which reside in the stack memory?
How does this work in actuality?
The second question is directly related and causes for me a personal dilemma (probably because I'm missing a point):
2) Which of the two approaches would you consult and why?:
NSString *myString = [[NSString alloc] initWithString: @"Value"];
NSString *myString = @"Value";
If my first question is confirmed, both approaches should "in the end" point to chars that are stored in the stack memory. I therefore don't actually see the purpose of using the first option and being bothered with the retain count.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
简短回答: 在这种情况下,两行具有相同的结果 将字符串常量直接分配给
myString
就可以了。更长的答案:
Objective-C 对象确实是在堆上分配的。然而,“原始”值并不总是存储在堆栈中。局部变量存储在堆栈中,无论它们是否是原始变量。 (例如,您可以声明一个结构体的局部变量,它不被视为原始值。)您可以将原始值存储在堆上,但在这种情况下访问它们的唯一方法是通过指针。例如:
我们总是使用指针来引用 Objective-C 对象的原因是对象总是在堆上分配。如果你想在堆栈上存储一些东西,编译器需要知道它的大小。在 Objective-C 中,直到程序实际运行时才知道对象的大小。
那么,回到你的弦上。尝试执行以下两行:
如果在第二行之后中断,您会发现
foo
和bar
包含相同的地址;也就是说,它们指向同一个对象。由于 NSString 对象是不可变的,因此使用常量字符串创建对象只会返回指向该常量的指针。如果 NSString 的作用就是如此,为什么还要有一个
-initWithString:
呢? NSString 是一个“类簇”,也就是说 NSString 是几个不同内部类的公共接口。如果将不是常量的 NSString* 传递给 -initWithString:,则返回的对象可能是与使用该常量时获得的对象不同的类的实例。作为一个类集群,NSString 向您隐藏了许多实现细节,以便您在不同类型的字符串上获得良好的性能,而不必担心它是如何工作的。Short answer: In this case, both lines have the same result It's fine to assign the string constant directly to
myString
.Longer answer:
It's true that Objective-C objects are allocated on the heap. It's not true, though, that "primitive" values are always stored on the stack. Local variables are stored on the stack, whether they're primitive or not. (You can, for example, declare a local variable that's a struct, which isn't considered primitive.) You can store primitive values on the heap, but the only way to access them in that case is via a pointer. For example:
The reason that we always use pointers to refer to Objective-C objects is that objects are always allocated on the heap. If you want to store something on the stack, the compiler needs to know its size. In Objective-C, the size of an object isn't known until the program is actually running.
So, back to your strings. Try executing the following two lines:
If you break after the second line, you'll find that
foo
andbar
contain the same address; that is, they point to the same object. Because NSString objects are immutable, creating one with a constant string just returns a pointer to that constant.Why is there even an
-initWithString:
in NSString if that's all it does? NSString is a "class cluster," which is to say that NSString is the public interface to several different internal classes. If you pass a NSString* that's not a constant into-initWithString:
, the object you get back might be an instance of a different class than what you get when you use the constant. As a class cluster, NSString hides a lot of implementation details from you so that you get good performance for different types of strings without having to worry about how it all works.NSString 很有趣,因为直到最近它们还是唯一可以作为文字提供的 Objective-C 对象类型。 iOS 4 和 OS X 10.6 中添加的块对象是我所知道的另一个最近添加的内容,但它们有自己的特定规则,所以我提到这一点只是为了完整性。
C 原语可以存储在堆或堆栈上。例如:
大多数 Objective-C 对象只能存储在堆上。你说的非常正确,
alloc
在堆上提供了一个对象的新副本,并且myString
调用的结果将是一个指向 NSString 的指针在堆上。但是,
@""
语法是创建对象的简写。@"Value"
实际上创建了一个对象文字。因此,例如,您可以这样做:输出将为“alue”。您可以将
substringFromIndex:
消息发送到@"Value"
,因为它是一个文字对象。NSString 与 initWithString: 的具体作用是特定于实现的,但您可以确定它会在需要时获取所指向事物的副本。否则,如果您执行以下操作,您会看到奇怪的行为:
因此您不必担心传递给它的任何内容的生命周期。 NSString 的工作就是处理这个问题。
实际上,
NSString
对象文字实际上永远不会过期,因此这一点有点没有实际意义。但是,如果您使用了initWithUTF8String:
或其他接受 C 文字的东西,并且该 C 文字位于堆栈上,您仍然无需担心,因为NSString
会处理它。在回答你的第二个问题时,我更喜欢第二个版本,因为它更短,因此更清楚地表明你打算做什么。后者有一些理论上的性能优势 - 特别是如果您在多个地方使用相同的文字 - 但它们是如此微不足道,以至于现在不值得考虑。
NSString
s are interesting because they were until recently the only type of Objective-C object that could be provided as a literal. The block objects added in iOS 4 and OS X 10.6 are the other recent addition that I'm aware of, but they have their own particular rules so I mention that just for completeness.C primitives can be stored on the heap or on the stack. For example:
Most Objective-C objects can be stored on the heap only. You're quite right to say that
alloc
furnishes a new copy of an object on the heap, and the result of yourmyString
call will be to have a pointer to an NSString on the heap.However, the
@""
syntax is shorthand for creating an object.@"Value"
actually creates an object literal. So, for example, you could do:And the output would be 'alue'. You can send the
substringFromIndex:
message to@"Value"
because it's a literal object.Exactly what
NSString
does withinitWithString:
is an implementation specific but you can be certain that it will take a copy of the thing pointed to if it needs to. Otherwise you'd see odd behaviour if you did something like this:You therefore needn't worry about the lifetime of anything you pass to it. It's
NSString
's job to deal with that.In practice,
NSString
object literals never actually expire so the point is a little moot. If however you'd usedinitWithUTF8String:
or something else that takes a C literal, and that C literal was on the stack, you'd still have nothing to worry about becauseNSString
would deal with it.In answer to your second question, I'd favour the second version on the grounds that it's shorter and therefore more clearly demonstrates what you intend to do. There are some theoretical performance benefits to the latter — especially if you use the same literal in multiple places — but they're so incredibly negligible as not to be worth considering nowadays.