【问题标题】:Are NSStrings stored on the heap or on the stack and what is a good way to initialize oneNSStrings 是存储在堆上还是堆栈上,什么是初始化的好方法
【发布时间】:2011-09-11 04:12:19
【问题描述】:

我有 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"; 

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

【问题讨论】:

    标签: objective-c memory nsstring theory


    【解决方案1】:

    简答:在这种情况下,两行的结果是一样的,直接把字符串常量赋值给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 对你隐藏了很多实现细节,这样你就可以在不同类型的字符串上获得良好的性能,而不必担心它是如何工作的。

    【讨论】:

      【解决方案2】:

      NSStrings 很有趣,因为直到最近它们还是唯一可以作为字面量提供的 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 会处理它。

      在回答您的第二个问题时,我倾向于第二个版本,因为它更短,因此更清楚地展示了您打算做什么。后者有一些理论上的性能优势——特别是如果你在多个地方使用相同的文字——但它们是如此微不足道,以至于现在不值得考虑。

      【讨论】:

        猜你喜欢
        • 2019-12-02
        • 2013-03-07
        • 2017-05-27
        • 2010-11-06
        • 2014-04-01
        • 2013-04-11
        • 2011-04-08
        • 2016-12-27
        • 2021-06-19
        相关资源
        最近更新 更多