具有读写和只读属性的 'property'、'_property'、'self.property' 和 'self._property' 之间有什么区别?

What is the difference between 'property', '_property', 'self.property' and 'self._property' with read-write and readonly properties?

我想很好地理解它,但到目前为止,我没有找到在一个地方考虑​​所有可能性的复杂答案。我知道在现代 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. 为什么 4、8、12、16 属性找不到,而 2、6、10、14 存在?
  2. 10 和 11 或 14 和 15 有什么区别?
  3. 我在 1 中实际做了什么?
  4. 我在哪里声明属性 2、6、10、14?
  5. 为什么我可以在 2、6 中分配只读属性,但在 3、7 中不能?

当你输入

self.title = @"foo";

对于

来说真的是shorthand
[self setTitle:@"foo" ];

你没有实施

-(void)setTitle:(NSString *)title 

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

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

所以你看到_title是一个实例变量。标题不是,它是returns一个NSString的方法。已经给你合成了,不过长这个样子

-(NSString *)title{
return _title;
}

它们是根本不同的东西。如果 title 是只读的,那么 setTitle: 方法永远不会被合成,尽管 _title iVar 仍然存在。因此,当您键入 self.title = @"bar" 时,编译器会查找 -(void)setTitle .. 但不存在。 -> 不开心的编译器

最佳做法是避免直接使用 _underScore 变量(称为支持 iVars)。始终使用 self.title = @"some 标题”。如果你想使用键值观察,这是可可最好的功能之一,你会很高兴你做到了。

开头的一些解释

一个。 @property 声明 方法,名称为-property,如果不是只读,则为-setProperty:@property 自己什么都不做!

乙。 @property 合成 ivar。什么?再说一遍! @property 合成 ivar。 (我可以重复多次。)

Ivars 是合成的,当你使用一个显式的 @synthesize 或 - 这使得它有点难以理解 - 当你有自动合成时。但是仍然有一个(隐含的,未显示的)综合!

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

  • 你需要一个@属性.
  • 没有手动实现访问器。

由于在源代码中可以看到第一个条件而看不到第二个条件,因此很容易假设@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,如果你已经有合适的。但是我们这里不需要。)

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

D.点表示法 而不是 访问 ivar。它发送一条消息。如果语句是左值,则使用 setter,如果它是右值,则使用 getter。你可以简单地翻译一下:

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

Why 4, 8, 12, 16 properties not found while 2, 6, 10, 14 exists?

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

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

What is the difference between 10 and 11 or 14 and 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 时还有其他事情要做,结果会有所不同。

What I actually do in 1?

title不是ivar(ivar的标识符是_title),而是参数var。 (看方法定义的head。)你把参数var的值赋给自己,什么意思啊。

您没有为 ivar 分配任何内容,因为您没有使用 ivar。

Where did I declare properties 2, 6, 10, 14?

参见上面的 A. 和 B.

Why I can assign to readonly properties in 2, 6 but not in 3, 7?

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

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

在3、7你发送消息给一个不存在的方法,因为属性是只读的。