Objective-c-NSMutableString设置字符串与NSString

Objective-c-NSMutableString设置字符串与NSString,objective-c,nsstring,release,retain,nsmutablestring,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

在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 *
}

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

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

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

- (void) init {
    if (self = [super init]) {
        m_name = [[NSMutableString alloc] init];
    }
}

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

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

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

- (void) init {
    if (self = [super init]) {
        m_name = [[NSMutableString alloc] init];
    }
}
结论:第一种解决方案肯定更好。

一些想法:

  • 最佳实践是根本不编写setter,以利用自动合成的访问器方法)。编写自己的程序只是一个扰乱内存管理或引入bug的机会。在编写自定义setter之前,您应该迫切需要自定义setter

  • 实例变量名的新兴约定是使用带前导下划线的属性名(例如,对于名为
    name
    的属性,ivar是
    \u name
    )。如果省略
    @synthesis
    语句,最新版本的Xcode附带的编译器将自动为您执行此操作

  • 如果没有说明属性具有哪些内存限定符,那么setter应该是什么的问题就没有意义。我假设您将属性定义为
    retain

  • 将属性更改为
    NSMutableString
    会更改属性的行为,我不建议您这样做,除非您出于某种原因确实需要可变字符串

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

  • 因此,我假设您有一个定义如下的属性:

    @property (nonatomic, retain) NSString *name;
    
    一条合成线被省略或看起来像:

    @synthesize name = _name;
    
    然后,我想二传手会是这样的:

    -(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];
        }
    }
    
    -(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];
        }
    }
    
    顺便说一下,我假设您的
    init
    方法正确地初始化了
    \u name
    ,并且
    dealloc
    释放了它

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

    正如bblum所指出的,谨慎的做法是对
    NSString
    属性使用
    copy

    @property (nonatomic, copy) NSString *name;
    
    然后,我想二传手会是这样的:

    -(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];
        }
    }
    
    -(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];
        }
    }
    
    但实际上,你不应该写二传,除非你绝对需要


    最后,您的代码有一条评论,指出内存管理令人困惑。虽然你肯定需要理解它,但我还是要提出最后两个建议:

  • 考虑使用(ARC)。虽然这并不排除理解内存管理工作原理的需要(请参阅),但它确实使编写正确处理内存管理的代码变得更容易。如果您编写手动引用计数(MRC)代码,很容易犯ARC会为您解决的简单内存管理错误

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

  • 有几点想法:

  • 最佳实践是根本不编写setter,以利用自动合成的访问器方法)。编写自己的程序只是一个扰乱内存管理或引入bug的机会。在编写自定义setter之前,您应该迫切需要自定义setter

  • 实例变量名的新兴约定是使用带前导下划线的属性名(例如,对于名为
    name
    的属性,ivar是
    \u name
    )。如果省略
    @synthesis
    语句,最新版本的Xcode附带的编译器将自动为您执行此操作

  • 如果没有说明属性具有哪些内存限定符,那么setter应该是什么的问题就没有意义。我假设您将属性定义为
    retain

  • 将属性更改为
    NSMutableString
    会更改属性的行为,我不建议您这样做,除非您出于某种原因确实需要可变字符串

  • 如果有人将
    name
    属性设置为
    nil
    ,则第一个示例不会执行任何操作。但是如果有人想将其设置为
    nil
    ,您仍然应该(a)释放旧的
    name
    值;和(b)将ivar设置为
    nil
    。(顺便说一下,我下面的代码利用了