属性与实例变量

时间:2009-04-05 22:06:04

标签: iphone objective-c cocoa cocoa-touch

我试图理解一些人用来区分实例变量和属性的策略。常见的模式如下:

@interface MyClass : NSObject {
    NSString *_myVar;
}

@property (nonatomic, retain) NSString *myVar;
@end

@implementation MyClass
@synthesize myVar = _myVar;

现在,我认为这个策略背后的整个前提是可以很容易地区分出伊达和财产之间的区别。因此,如果我想使用由合成属性继承的内存管理,我会使用如下内容:

myVar = @"Foo";

另一种方式是通过自我引用它。[ivar / property here]。

使用@synthesize myVar = _myVar策略的问题是,我认为编写代码如:

myVar = some_other_object; // doesn't work. 

编译器抱怨myVar未声明。为什么会这样?

感谢。

7 个答案:

答案 0 :(得分:28)

属性只是ivars的设置者和获取者,应该(几乎)始终使用而不是直接访问。

@interface APerson : NSObject {
    // NSString *_name;           //  necessary for legacy runtime
}

@property(readwrite) NSString *name;

@end

@implementation APerson
@synthesize name;                 // use name = _name for legacy runtime
@end
在这种情况下,

@synthesize会创建这两种方法(不是100%准确):

- (NSString *)name {
    return [[_name copy] autorelease];
}
- (void)setName:(NSString *)value {
    [value retain];
    [_name release];
    _name = value;
}

现在很容易区分ivars和getters / setters。访问者获得了self.前缀。你不应该直接访问变量。


您的示例代码无法正常运行:

_myVar = some_other_object;      // _myVar is the ivar, not myVar.
self.myVar = some_other_object;  // works too, uses the accessors

答案 1 :(得分:8)

名为prop的合成属性实际上由两个方法prop(返回属性的当前值)和setProp:(为prop设置新值)表示。

self.prop语法是用于调用其中一个访问器的语法糖。在您的示例中,您可以执行以下任一操作来设置属性myVar

self.myVar = @"foo"; // handles retain/release as specified by your property declaration
[self setMyVar: @"foo"]; // handle retain/release
_myVar = @"Foo"; // does not release old object and does not retain the new object

要访问媒体资源,请使用self.propname。要访问实例变量,只需使用实例变量的名称。

答案 2 :(得分:5)

  

使用@synthesize myVar = _myVar策略的问题是,我认为编写代码如:

myVar = some_other_object; // doesn't work.
     

编译器抱怨myVar未声明。为什么会这样?

因为变量myVar未声明。

该语句使用语法来访问变量,无论是实例变量还是其他类型。正如rincewind告诉你的那样,要访问属性,你必须使用属性访问语法(self.myVar = someOtherObject)或显式消息来访问访问方法([self setMyVar:someOtherObject])。 / p>

否则,您正在尝试访问变量,并且由于您没有名为myVar的变量,因此您尝试访问不存在的变量。

答案 3 :(得分:2)

通常,我将我的属性命名为与实例变量相同;这是@property语法的默认假设。如果你发现你正在打击默认值,那么你做错了(或者你的框架sux,在我看来,这不是Cocoa / Cocoa-touch的情况)。

你得到的编译器错误是因为属性使用总是必须有一个对象引用,即使在你自己的类实现中:

self.stuff = @"foo"; // property setter
[stuff release]; // instance variable
stuff = @"bar"; // instance variable
return self.stuff; // property getter

我知道许多Cocoa程序员不同意,但我认为在类实现中使用属性是不好的做法。我宁愿看到这样的东西:

-(void) someActionWithStuff: (NSString*) theStuff {
    // do something
    [stuff release];
    stuff = [theStuff copy];
    // do something else
}

比这个:

-(void) someActionWithStuff: (NSString*) theStuff {
    // do something
    self.stuff = theStuff;
    // do something else
}

我更喜欢尽可能明确地进行内存管理。但即使您不同意,使用self.stuff表单也会让任何有经验的Objective-C程序员知道您正在调用属性而不是访问实例变量。对于初学者来说,这是一个很容易被忽略的微妙点,但是在你使用Objective-C 2.0一段时间后,它已经非常清楚了。

答案 4 :(得分:0)

唐,

根据“规则”,您应该为每个Copy,Alloc和Retain调用Release。那么你为什么要调用Release呢?这是假设它是使用Alloc,Copy还是Retain创建的?

这提出了另一个问题:如果对象的引用已经被释放,那么调用Release是否有害?

答案 5 :(得分:0)

由于Apple为自己保留了_前缀,因为我更喜欢在使用setter时更明显,当我使用ivar时,我采用了在我的ivars上使用i_的前缀,例如:

@interface MyClass : NSObject {
    NSString *i_myVar;
}

@property (nonatomic, retain) NSString *myVar;

@synthesize myVar = i_myVar;

i_myVar = [input retain];
self.myVar = anotherInput;
[i_myVar release]

由于知道何时使用setter以及使用ivar非常重要,因此我发现明确不同的名称更安全。

在你的问题中,它应该是:

self.myVar = @"Foo"; // with setter, equivalent to [self setMyVar:@"Foo"]

_myVar = some_other_object;  // direct ivar access - no memory management!

请记住,您不应该在init / dealloc中使用setter / getters,因此您需要在这些方法中进行直接的ivar访问(以及仔细的内存管理)。

答案 6 :(得分:0)

简单地使用

有什么问题
@interface MyClass : NSObject
@property NSString *prop;
@end

非原子和保留不是必需的,保留是默认值,原子/非原子并不重要,除非XCode告诉你一个警告。

没有必要声明iVar,如果你真的想使用iVar,你将为你创建名为_prop的那个(我不明白为什么说实话)

不需要@synthesize。

当(并且您应该)使用ARC时,您不必费心保留和释放。

保持简单!

此外,如果你有像这样的方法

- (void)aMethod:(NSString*)string
{
    self.prop = string;
    // shows very clearly that we are setting the property of our object

    _aName = string;
    // what is _aName ? the _ is a convention, not a real visual help
}

我总是会使用属性,更灵活,更容易阅读。