iOS中,使用下划线和直接使用iVar有何区别?

9

这个问题问得很多,但是这个问题是为了得到每种方法何时使用的示例。 请使用除setter和getter无限循环之外的示例。

例如:

.h -
@property(nonatomic, strong)NSMutableArray* mutArray
.m -
@synthesize mutArray= _mutArray;

1)我该选择:
_mutArray = [[NSMutableArray alloc] init];
还是
self.mutArray=[[NSMutableArray alloc] init];
我为什么要这样做,它们有什么区别?!

2)如果我想要向其中添加一个对象...
[_mutArray addObject:object];
还是
[self.mutArray addobject:object];

为什么这样做?!

非常感谢!

3个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
13
您应该只在initdealloc中处理ivars,或者在实现细节(例如在访问器本身内部或实际需要内存地址的地方)绝对需要时。除了这些地方,您应始终使用访问器,这意味着使用[self foo]而不是_fooself.foo只是语法糖,其实际调用是[self foo]。重要的是要理解self.foo是标准的ObjC消息发送,并且与[self foo]完全相同。根据惯例,只有在引用属性时才使用点语法。 在ARC之前,直接使用ivars是我经验中导致崩溃的主要原因。如果没有ARC,您直接分配到ivar的可能性很快会超过程序范围的100%。 自ARC以来,我仍然认为您应始终使用访问器(除非有上述例外),但原因更加微妙。主要原因是访问器可以定制,无论是在当前类中,还是在子类中,或者通过KVO(它在您的代码之外发生)。如果直接访问ivar,则将绕过此功能。例如,假设该属性是延迟创建的(这很常见)。那么,如果在创建之前使用ivar,您将遇到微妙的错误。因此,您必须记住,对于该属性,始终使用访问器。类似地,您可能会调用setNeedsDisplay或发布通知等。 如果您有一个简单的规则,它说“我将始终使用访问器”,那么查看代码并知道它是正确的就很容易。在极少数情况下需要规避访问器时,“_”表示“嘿,在这里注意一下,我正在做一些奇怪的事情。”如果你有一个规则:“只有需要它的属性才使用访问器,不需要的就不用”,那么很难从代码中判断它是否正确。之前的开发人员是因为必须要用还是仅仅是个人喜好而使用了ivar?你能否更改它呢?这很难知道。 所以即使在ARC之后,始终使用访问器是良好的防御性编程,我强烈推荐这样做。

这与Ragnar的答案非常矛盾!您认为通过使用setter添加额外的保留是否有意义?还有其他人可能正在使用代码并更改setter/getter,导致意外结果的事实如何? - jgvb
“extra” retain 不是“额外的”。这是您必须应用的所需保留,并且访问器是正确的应用位置(仅适用于 pre-ARC,但未能正确应用该保留是我提到的崩溃的主要原因)。您希望使用访问器,恰好是因为其他人可能正在处理代码,并且没有办法确保他们都知道并始终遵循“当您更改此 ivar 时,您还必须...”正确记录自定义要求的方法是通过访问器。 - Rob Napier
偶尔这会导致意想不到的结果(例如过于频繁地重新计算某些东西),但在我的经验中,这些副作用几乎总是比你不知道在设置 ivar 后必须调用 setNeedsDisplay 更容易调试。这会导致一些“有点工作,除非它们不工作”的事情,这些事情极难调试。当你不知道它会发生时运行访问器会显示在堆栈跟踪中。未能运行您不知道需要运行的访问器...那很难找到。 - Rob Napier
所以,只是为了澄清,在我的问题中的addobject示例中,除非getter/setter被修改,否则这两个将完全做相同的事情?在[self.arr addobject:object];中实际调用的是getter还是setter? - jgvb
它们将几乎执行相同的操作。第二个不会调用[self mutArray],而第一个则会。默认getter实际上具有原子包装器,这是不同的。您的注释示例可以重写为[[self arr] addObject:object]。(这正是我们在点语法之前编写它的方式。) - Rob Napier

0

_mutArray是iVar,self.mutArray通过getter和setter访问属性,当您使用@synthesize属性时,这些getter和setter会为您创建。您可以重写这些getter和setter以执行自定义操作。在您的示例中,您的属性设置为strong,这会将retain添加到您的属性中。因此,self.mutarray = [[NSMutableArray alloc] init];会执行类似以下操作(除非您覆盖了属性的setter):

-(void)setMutarray:(NSMutableArray*)_mutArray{
    if(_mutArray != mutArray){
        [mutArray release];
        mutArray = nil;
        mutArray = [_mutArray retain];
    }
}

如果要将对象添加到数组中,您只需要访问iVar,而不是属性,除非您在getter中进行自定义操作。例如,您的getter可能如下所示:

-(NSMutableArray*)mutArray{
    if(!mutArray){
        self.mutArray = [[[NSMutableArray alloc] init] autorelease];
    }
    return mutArray;
}

通过这种方式,您可以确保始终有一个真实的数组来添加对象。如果是这种情况,您将想要使用 [self.mutArray addObject:object];

因此,除非您想在getter或setter中执行自定义操作,否则只需访问iVar。


除非我进行自定义,否则为什么不想通过setter和getter? - jgvb
你不想通过 setter 进行操作,因为它会添加一个 retain。而且,如果有其他人在使用该代码,他们可能正在 getter 或 setter 中进行某些操作,这是你没有考虑到的。 - Kelly Bennett
我不是专家,但我尊重地不同意。在非ARC环境中,你需要那个retain - 它不是多余的。在ARC中,这并不重要。另一件需要考虑的事情是:如果你没有使用访问器,你必须始终检查你的iVar是否在使用之前实例化(如果没有,则进行alloc/init)。甚至不用考虑KVO的东西,这就为始终使用setter/getter提供了有力的论据。 - chris stamper

0

self是类似于JAVA中的thiskeyword的关键字。

它访问当前对象并且是一个指向对象的指针。

有关self更多信息,请阅读本教程。

_ObjeNameObjective C中称为iVar

实现中声明的实例变量默认情况下是隐藏的(事实上是私有的),可见性不能被改变,- @public, @protected和@private不会产生编译错误(至少在当前的Clang版本中如此),但是会被忽略。

例如:

1)NSString *someString = _name;

2)NSString * someString = self.name;

假设您在.m文件中有这行代码(并且没有任何重写方法来直接访问_name):
@synthesize name = _name;

这意味着当您尝试访问属性name(self.name)时,它将使用变量_name。在这种情况下,self.name等于_name。


但是如果您的名称具有动态属性,类似于这样:

-(NSString)name{
    return @"1234";
}

那么就有所不同了。self.name总是会返回1234,但是_name可能与这个值不相等。

例如:

_name = @"555";
NSLog(_name);
NSLog(self.name);

结果:

2012-02-09 14:27:49.931 ExampleApp[803:207] 555
2012-02-09 14:27:49.933 ExampleApp[803:207] 1234

我从这个问题中得到的上面的例子。


2
对于新开发者来说,将“self”比作“this”非常令人困惑。在Java中,“this”可以被隐含。但在ObjC中,“self”从不被隐含。这导致了很多混淆,当以前的Java开发者认为“_name”和“self.name”意味着完全相同的事情时。它们永远不会相同,它们总是会生成不同的汇编代码,只是可能会返回相同的结果。 - Rob Napier
@RobNapier +1 . 我曾经从Java背景转到ObjC,也和你一样在比较thisself,虽然它们有关联,但是它们是不同的东西。 - Petar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,