代码之家  ›  专栏  ›  技术社区  ›  Sixto Saez

StringWithString中的对象所有权和NSString中的InitWithString

  •  9
  • Sixto Saez  · 技术社区  · 16 年前

    我知道任何初始…方法初始化一个新对象,并且nsstring stringwithstring将参数字符串的副本复制为一个新对象。我还了解,作为对象的所有者,我可以控制我分配的任何对象的释放/解除分配。我不明白的是,什么时候会使用StringWithString方法,因为以这种方式分配的任何局部变量的内存都将由nsString“拥有”,而不是本地类。

    Kochan(第1版)的《目标C中的编程》一书使用以下代码(见342-344页)来解释initwithstring比stringwithstring更好,因为addresscard类将拥有名称变量内容。此外,使用StringWithString方法重复调用setName版本时不会出现任何错误。蒂亚!!

    //header file has appropriate declarations but not included here:
    #import "AddressCard.h"
    
    @implementation AddressCard;
    
    -(NSString *) name
    {
       return name;
    }
    
    //Recommended code:
    -(void) setName: (NSString *) theName
    {
       [name release]
       name = [[NSString alloc] initWthString: theName];
    }
    
    //Incorrect code according to Kochan:
    -(void) setName: (NSString *) theName
    {
       [name release]
       name = [NSString stringWthString: theName];
    }
    
    //rest of class implementation code snipped
    @end
    
    5 回复  |  直到 13 年前
        1
  •  27
  •   Peter Hosey    14 年前

    我不明白的是,什么时候会使用StringWithString方法,因为以这种方式分配的任何局部变量的内存都将由nsString“拥有”,而不是本地类。

    什么?不。

    规则很简单:

    • 返回的任何对象 alloc , copy , copyWithZone new 保留计数为1。
    • retain 增加接收对象的保留计数。
    • release 减少接收对象的保留计数。
    • autorelease 通知当前自动释放池发送接收对象 释放 信息_稍后__。
    • 名称中没有__new_或__copy_157;的任何工厂方法(例如, stringWithString: )返回已代表您自动释放的对象。

    或者,消化一点:

    • 名称中包含的任何方法 复制 , 同种异体 , 保持 新的 返回您拥有的对象。
    • 任何不属于的方法都会返回不属于您的对象。
    • 要拥有一个对象,请保留它。

    错误的执行 setName: 您所显示的是不正确的,因为当您打算拥有对象时,它将自动释放的对象存储在实例变量中。你应该保留它,或者,在这种情况下,复制它。一种方法是简单地使用 同种异体 initWithString: 如您所示的正确示例;另一种方法是 复制 .

    The Memory Management Programming Guide for Cocoa explains everything. 每个Cocoa或Cocoa Touch程序员都应该不时地阅读或重新阅读它。

        2
  •  6
  •   Boaz Stuller    16 年前

    事实上,两个二传手都错了。“不正确”一词是错误的,因为一般的内存管理原因(在其他地方有很好的阐述)。“推荐”的错误有两个原因:

    1. 如果(thename==name),那么 可能会在 第一行,然后尝试 将释放的对象用作 -initWithString的参数:在 第二行,导致未定义 行为。
    2. -initWithString:不能优雅地处理为零传递。

    “正确”(imho)方法是:

    -(void) setName: (NSString *) theName
    {
       if (theName == name) return; // if they're equal, no need to do anything further
       [name release];
       name = [theName copy];  // sets name to nil if theName is nil
    }
    

    对于大多数对象,您实际上希望在第三行保留而不是复制,但对于字符串,复制几乎总是更好的。

        3
  •  3
  •   Community CDub    8 年前

    initWithString和stringWithString的区别在于,stringWithString返回一个自动释放的指针。这意味着您不需要特定地释放它,因为下次自动释放池清除任何自动释放的指针时都会注意到这一点。

    另一方面,initwithstring返回一个保留计数为1的指针-您需要调用该指针上的release,否则它将导致内存泄漏。

    https://stackoverflow.com/questions/193288/what-is-the-cost-of-using-autorelease-in-cocoa 因为一些原因,比如为什么你应该使用自动释放和释放。

        4
  •  0
  •   Ben Gottlieb    16 年前

    在上面不正确的代码中,下次调用setname之后引用name时,会出现异常错误,因为对象将被释放。您可以使用“正确”代码,也可以使用显式retain调用包装StringWithString调用:

    name = [[NSString stringWithString: theName] retain];
    
        5
  •  0
  •   Matt Gallagher    16 年前

    我不明白的是,什么时候会使用StringWithString方法,因为以这种方式分配的任何局部变量的内存都将由nsString“拥有”,而不是本地类。

    使用创建的字符串 stringWithString: 不属于 NSString ,它属于 NSAutoreleasePool (尽管多个地方可以 retain 使所有权共享的对象)。

    带字符串的字符串: ,下次处理自动释放池时(通常在应用程序的下一个事件循环期间),字符串将变为无效,因为NSautoReleasePool将 release 它的指针。如果你没有 保持 在那之前,把字符串,任何指向它的指针( name 在类的情况下)将无效(变量 名称 将仍然存在,但它将指向垃圾)。

    如果您不打算保留任何指向 非字符串 但既然你真的打算保留一个指针,你就需要 保持 这个 非字符串 . initWithString: 自动为您提供保留计数1。