' property',' _property',' self.property'有什么区别?和' self._property'具有读写和只读属性?

时间:2015-01-16 11:04:41

标签: objective-c variables properties initialization

我想非常理解,但到目前为止,我并没有在一个地方考虑​​所有可能性而找到复杂的答案。我知道在现代objective-c中,我们不会在@implementation Album之后的{ }之后创建ivars。

我刚刚为测试创建了 Album.h

@interface Album : NSObject

@property (nonatomic, copy, readonly) NSString *title, *artist;
@property (nonatomic, copy) NSString *title2, *artist2;

- (id)initWithTitle:(NSString*)title;

@end

Album.m

@implementation Album

- (id)initWithTitle:(NSString*)title {
    self = [super init];
    if (self) {

        //READ-ONLY
        title = title; //1, still nil after compile
        _title = title; //2,
        self.title = title; //3, "assignment to readonly property"
        self._title = title; //4, "property '_title' not found"

        artist = @"Shakira"; //5, "use of undeclared identifier 'artist'"
        _artist = @"Shakira"; //6
        self.artist = @"Shakira"; //7, "assignment to readonly property"
        self._artist = @"Shakira"; //8, "property '_artist' not found"

        //READ-WRITE
        title2 = title; //9, "use of undeclared identifier 'title2'"
        _title2 = title; //10
        self.title2 = title; //11
        self._title2 = title; //12, "property '_title2' not found"

        artist2 = @"Shakira"; //13, "use of undeclared identifier 'artist2'"
        _artist2 = @"Shakira"; //14
        self.artist2 = @"Shakira"; //15
        self._artist2 = @"Shakira"; //16, "property '_artist2' not found"

    }
    return self;
}

@end

现在我用它了:

Album *album = [[Album alloc] initWithTitle:@"Live from Paris"];

问题是:

  1. 为什么在2号,6号,10号,14号时找不到4,8,12,16个属性?
  2. 10和11或14和15之间有什么区别?
  3. 我在1中实际做了什么?
  4. 我在哪里声明属性2,6,10,14?
  5. 为什么我可以在2,6中分配只读属性,但不能分配到3,7?

2 个答案:

答案 0 :(得分:4)

输入

时输入

self.title = @"foo";

这是

的简写
[self setTitle:@"foo" ];

你没有实施

-(void)setTitle:(NSString *)title 

编译器已经为你合成了它。如果你能看到它放在那里它可能看起来像这样

-(void)setTitle:(NSString *)title{
_title = [title copy];
}

所以你看_title是一个实例变量。 title不是,它是一个返回NSString的方法。它已经为你合成了,但它看起来像这样

-(NSString *)title{
return _title;
}

它们是根本不同的东西。如果title是readonly,那么setTitle:方法永远不会被合成,尽管_title iVar仍然存在。所以当你输入self.title = @" bar"编译器查找 - (void)setTitle ..哪个不存在。 - >不开心的编译器

最佳做法是避免直接使用_underScore变量(称为支持iVars)。总是使用self.title = @"一些 标题"代替。如果您想使用键值观察,这是可可最佳功能之一,那么您会很高兴。

答案 1 :(得分:4)

开头的一些解释

一个。 @property 使用名称-property声明 方法,如果不是只读,则-setProperty:@property本身没有做任何事情!

B中。 @property 合成一个ivar。什么?再说一遍! @property 合成一个ivar。 (我可以多次重复这一点。)

当您使用显式@synthesize或 - 并且这使得它有点难以理解 - 当您进行自动合成时,会合成Ivars。但是仍然存在一种(隐含的,未示出的)合成!

要进行隐式合成,必须满足以下条件:

  • 你需要一个@property。
  • 手动实施访问者。

由于您可以在源代码中看到第一个条件并且看不到第二个条件,因此很容易假设@property合成了ivar。但事实并非如此。我们来试试吧:

@interface Foo : NSObject
@property NSString *foo;
@end

@implementation Foo
// Both accessors are implemented -> no auto synthesize -> no ivar
- (NSString*)foo
{
  return _foo; // Error: No ivar _foo
}

- (NSString*)setFoo:(NSString*)foo
{
  _foo=foo; // Error: No ivar _foo
}
@end

原因是:如果@property本身合成了ivar,那么就不可能有一个没有ivar支持的声明属性。

(另外没有合成的ivar,如果你已经有一个适合的话。但我们在这里不需要它。)

℃。合成的ivar具有标识符_property,而不是property。 (在某些情况下,没有下划线的现有ivar将被视为已经存在的ivar,但我们也需要这里。)

d。点符号访问ivar。它发送一条消息。如果语句是左值,则使用setter,如果是rvalue,则使用getter。你可以简单地翻译一下:

self.property = …; // [self setProperty:…];
… = self.property; // … = [self property];
  

为什么在2号,6号,10号,14号时找不到4,8,12,16个属性?

在4,...您使用点表示法,使用选择器set_Property发送消息。没有这样的方法,因为合成的方法是-setProperty::错误

在2,...你直接访问ivars。它的标识符是_property。一切都很好。

  

10和11或14和15之间有什么区别?

在11,15,您使用setter设置属性的值。在10,14,你直接设置了ivar。不执行setter的代码。

我们举个例子:

@interface Foo : NSObject
@property NSString *foo;
@end

@implementation Foo
// ivar _foo is (auto) synthsized, because there is no getter.
- (void)setFoo:(NSString*)foo
{
  NSLog( @"Setter executed");
  _foo = foo;
}
@end

在第10,14行,您将看不到日志,因为未执行setter。在第11行,第15行,您将看到一个日志。

如果您在setter(或getter)中还有其他事情要做,只接受设置ivar,结果会有所不同。

  

我在1中实际做了什么?

title不是ivar(ivar的标识符是_title),而是参数var。 (查看方法定义的开头。)将参数var的值赋给自身,什么是无意义的。

您没有为ivar指定任何内容,因为您不使用ivar。

  

我在哪里声明属性2,6,10,14?

见上文A.和B.

  

为什么我可以在2,6中而不是在3,7中分配只读属性?

ivar永远不会读。 readonlyreadwrite选项用于方法声明。 (见上文A. - C.)

在2,6中你直接设置了ivar。它不能只读,所以一切都很好。

在3,7中,您向不存在的方法发送消息,因为属性是只读的。