【问题标题】:NSString Address issueNSString 地址问题
【发布时间】:2015-09-13 06:59:29
【问题描述】:

我正在尝试将地址打印到字符串,但我在第一个 NSLog 中得到不同的地址,在第二个 NSLog 中得到相同的地址。那么你能告诉我这是怎么回事吗?它真的让我很困惑。非常感谢您的努力。

NSString *str1 = [[NSString alloc] init];
NSString *str2 = [[NSString alloc] init];
NSString *str3 = [[NSString alloc] init];

NSLog(@"str1 = %p , str2 = %p, str3 = %p",&str1,&str2,&str3);
NSLog(@"str1 = %p , str2 = %p, str3 = %p",str1,str2,str3);

输出

str1 = 0x7fff565b9c88 , str2 = 0x7fff565b9c80, str3 = 0x7fff565b9c78
str1 = 0x10c0a7060 , str2 = 0x10c0a7060, str3 = 0x10c0a7060

我不明白为什么str1str2str3 都指向同一个内存位置。

【问题讨论】:

    标签: objective-c


    【解决方案1】:

    str1是字符串的内存地址

    &str1是指向字符串的指针的内存地址

    (可能反过来)

    【讨论】:

    • 但是为什么第二个 nslog 打印相同的结果。
    • 我猜,因为字符串是不可变的,无论如何使用相同的内存地址。
    • 但是我们创建了不同的 NSString 实例。所以它应该打印不同的地址,因为它在不同的地址上占用内存。
    • 是的,指针地址不同,但它们指向相同的内存地址(这表明&str 是内存地址)。这是安全的,因为字符串是不可变的
    • 这个答案很不完整。问题不是问值代表什么,而是问为什么三个指针都指向同一个地址。
    【解决方案2】:

    有趣的是,你得到了结果,但有意义的是 str1、str2、str3 都是不可变的,所以它们永远不会改变并且都具有相同的内容,所以你没有得到 3 个新的 NSString,而是得到了相同的 3 次.如果将它们更改为 NSMutableStrings,您将获得预期的结果。我又玩了一点,我发现如果你打开 ARC 并添加这个

    NSLog(@"str1 = %lu , str2 = %lu, str3 = %lu",
               [str1 retainCount], [str2 retainCount], [str3 retainCount] );
    

    你得到了一些更有趣的东西,我期待看到值 3,三次代表同一个对象的三个分配,但是你得到 18446744073709551615,当你也添加这个时这是有意义的

    NSLog(@"str1 = %@ , str2 = %@, str3 = %@",
                            [str1 class], [str2 class], [str3 class] );
    

    你会看到这个类是 __NSCFConstantString ,它没有被引用计数,它相当于 c 文字字符串。

    你可以在很多 c 编译器中得到类似的东西,如果你定义一个像这样的文字 c 字符串

        char    * cstr1 = "abc";
        char    * cstr2 = "abc";
    
        printf( "cstr1 == cstr2 = %s\n", cstr1 == cstr2 ? "true" : "false" );
    

    您可以看到编译器通过只有一个“abc”并将所有指针指向同一个位置来为文字节省内存。

    再想一想 NSString 的 init 方法可能看起来像这样

    - (instancetype)init {
        [self release];
        return @"";
    }
    

    【讨论】:

    • 您告诉 str1、str2 和 str3 都是不可变的,但我认为应该打印不同的地址,因为我们正在创建不同的实例。
    • 你不知道最终会创建什么,alloc init 模式通常分配并返回一个新对象,但它不必这样做,因为在我的 init 示例中,原始对象很可能被释放而是返回一个常量字符串。这就是为什么我们总是将 [super init] 的结果分配给 init 方法中的 self,以防超类返回与我们开始时不同的对象。这让你可以做一些有趣的事情,比如单例,或者每个键值一个唯一的实例等。
    • 我并不总是[self release],但当我这样做时,就会有[super init]
    • 我不确定你的意思,但你不应该在 init 方法之后使用 alloc 的结果,因为我已经展示了这种情况。
    【解决方案3】:

    对 NSLog 的第一次调用是打印 3 个局部变量 str1、str2 和 str3 的地址。它们都驻留在堆栈上,因为它们是本地的,因此地址很大。

    第二个 NSLog 调用打印 str1、str2 和 str3 所指向的对象的地址,在本例中,这些对象已针对同一对象进行了优化。

    您的局部变量已经是指向 NSStrings 的指针,而不是实际的 NSStrings 本身,因此您不需要地址运算符 &。

    【讨论】:

      【解决方案4】:

      为什么应该 str1str2str3 都驻留在不同的内存地址?它们都是相同的不可变字符串。

      bbum's comment here:

      是的...一个相关的实现细节(但无论如何都不会使答案无效); [[NSString alloc] initWithString:@"Hello world"] 实际上不会在堆上创建字符串。它只会返回编译器在 mach-o 文件中设置的 __NSCFConstantString (或其他任何名称)。 这只是一个有趣的细节,因为它不会改变您对 said 的使用的任何内容细绳;应该像对待任何其他对象一样对待它。

      强调我的。

      这里发生的情况是,当编译器可以在编译时确定不可变的NSString 对象将是什么时,它会以不同的方式创建该字符串。正如 bbum 所说,最终它是一个实现细节,您在编写程序时不应该担心。

      但是这样做的副作用意味着编译器能够使我的程序更有效地使用内存,因为它能够找到所有这些实例,并使我知道应该持有的所有 NSString 指针相同的不可变值都指向相同的单个内存地址。

      我们可以通过以下方式获得相同的结果:

      NSString *str1 = [[NSString alloc] init];
      NSString *str2 = [NSString new];
      NSString *str3 = [[NSString alloc] initWithString:@""];
      NSString *str4 = [NSString stringWithString:@""];
      NSString *str5 = @"";
      

      这些实际上都是一样的。

      但是,如果我们创建另一个字符串:

      NSString *str6 = [NSString stringWithFormat:@"%@", @""];
      

      如果我们将str6 打印为指针,这将(很可能......我上次检查时)以不同的值结束。

      还有其他方法可以生成不可变的NSString 对象,这些对象在编译时不会像这样进行优化。这里的要点是,如果编译器可以在编译时知道字符串将是什么,它将在内存管理之外的后台创建一个__NSCFConstantString,并且它将尽可能指向该单个实例。一旦它开始运行,它只会在你直接指向它的时候指向它(str6 = str1)。否则,它不会浪费执行时间来尝试确定字符串是否相等。如果一个新的NSString 恰好相等并且它没有在编译时发生,它将由 ARC 处理。

      编译器无法确定str6 是与其他字符串相同的不可变字符串。这只是在构建时暗示其他人都以相同的地址结束。

      要注意的另一件有趣的事情是,您永远不会看到在__NSCFConstantString 上调用dealloc,编译器正在为以您声明它们的方式声明的变量创建变量。因此,编译器不仅从内存的角度使您的代码更高效,它还删除了所有与这些字符串保持同步所涉及的内存管理代码。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-29
        相关资源
        最近更新 更多