Objective-c - NSMutableString setString vs NSString

时间:2013-05-30 13:46:37

标签: objective-c nsstring release retain nsmutablestring

在setter中保留和释放NSString是一种更好的做法,如下所示:

-(void) setName:(NSString *)newName
{
    if(newName != nil)
    {
         [newName retain]:
         [m_Name release];
         m_Name = newName; //Where m_Name is a NSString *
    }
    //I'm not sure for this code, I have difficulties understanding memory-management in ObjC
}

或者通过NSMutableString更改值:

-(void) setName:(NSString *)newName
{
    if(newName != nil)
        [m_Name setString:newName]; //Where m_Name is a NSMutableString *
}

如果任何或两种方法都不正确,请告诉我。

2 个答案:

答案 0 :(得分:3)

有几点想法:

  1. 最佳做法是不要编写setter,以利用自动合成的访问器方法。编写自己的内容只是搞乱内存管理或引入错误的机会。在编写自定义setter之前,您应该非常需要自定义setter。

  2. 实例变量名称的新兴惯例是使用前导下划线的属性名称(例如,对于名为name的属性,ivar为_name)。如果省略@synthesize语句,最近版本的Xcode中包含的编译器将自动为您执行此操作。

  3. 在你没有说明你的财产有什么记忆限定条件的情况下,设定者应该是什么的问题没有任何意义。我假设你将你的房产定义为retain

  4. 将属性更改为NSMutableString会更改属性的行为,除非您出于某种原因确实需要可变字符串,否则我不会建议。

  5. 如果有人将name属性设置为nil,则您的第一个示例不执行任何操作。但如果有人想将其设置为nil,您仍然应该(a)释放旧的name值; (b)将你的ivar设为nil。 (顺便说一下,我的代码利用了向nil对象发送消息什么都不做的事实,所以在这种情况下我不需要检查它是否nil。 )

  6. 所以,我假设你有一个如下定义的属性:

    @property (nonatomic, retain) NSString *name;
    

    和一个省略或看起来像的合成行:

    @synthesize name = _name;
    

    然后,我认为setter看起来像:

    -(void) setName:(NSString *)name
    {
        // if you want to program defensively, you might want the following assert statement:
        //
        // NSAssert(name == nil || [name isKindOfClass:[NSString class]], @"%s: name is not string", __FUNCTION__);
    
        if (name != _name)
        {
            [_name release];
            _name = name;
            [_name retain];
        }
    }
    

    顺便说一下,我假设您的init方法正确初始化_namedealloc释放它。

    - (id)init
    {
        self = [super init];
        if (self) {
            _name = nil;
        }
        return self;
    }
    
    - (void)dealloc
    {
        [_name release];
        [super dealloc];
    }
    

    正如bblum指出的那样,谨慎使用copyNSString属性:

    @property (nonatomic, copy) NSString *name;
    

    然后,我认为setter看起来像:

    -(void) setName:(NSString *)name
    {
        // if you want to program defensively, you might want the following assert statement:
        //
        // NSAssert(name == nil || [name isKindOfClass:[NSString class]], @"%s: name is not string", __FUNCTION__);
    
        if (name != _name)
        {
            [_name release];
            _name = [name copy];
        }
    }
    

    但实际上,除非你绝对需要,否则你根本不应该写限制器。


    最后,您的代码有一个关于查找内存管理令人困惑的评论。虽然你肯定需要理解它,但我会提出两个最终建议:

    1. 考虑使用Automatic Reference Counting(ARC)。虽然这并不能消除理解内存管理如何工作的需要(参见Advanced Memory Management Programming Guide),但它确实可以更容易地编写正确处理内存管理的代码。如果您编写手动引用计数(MRC)代码,则可以很容易地做出简单的内存管理错误,而ARC会为您解决这些错误。

    2. 特别是如果您要使用MRC,请使用Xcode的静态分析器(“产品”菜单上的“分析”或按 shift + 命令 + )。这有助于找到困扰MRC代码的许多常规内存管理问题。 仪器用户指南Finding Memory Leaks部分还说明了在调试代码时如何查找泄漏,但静态分析器通常只需检查代码即可识别问题。

答案 1 :(得分:0)

第一种解决方案更好。这就是retain属性的处理方式。保留新值,然后释放旧值。此外,如果nil案例并不重要,您可以依赖@synthesize生成的默认实现。

对于第二个解决方案,它实际上是不必要的,而且它有点违反惯例。此外,在此解决方案中,您必须在为其分配任何字符串之前初始化m_name。您可以在init

中执行此操作
- (void) init {
    if (self = [super init]) {
        m_name = [[NSMutableString alloc] init];
    }
}

结论:第一种解决方案肯定更好。