具有读写和只读属性的 '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"];
问题是:
- 为什么 4、8、12、16 属性找不到,而 2、6、10、14 存在?
- 10 和 11 或 14 和 15 有什么区别?
- 我在 1 中实际做了什么?
- 我在哪里声明属性 2、6、10、14?
- 为什么我可以在 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 永远不是只读的。 readonly
和 readwrite
选项用于方法声明。 (见上文 A.-C.)
2、6中直接设置了ivar。不能只读,所以一切都很好
在3、7你发送消息给一个不存在的方法,因为属性是只读的。
我想很好地理解它,但到目前为止,我没有找到在一个地方考虑所有可能性的复杂答案。我知道在现代 objective-c 中,我们不会在 @implementation Album
之后在 { }
.
只是为了我创建的测试 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"];
问题是:
- 为什么 4、8、12、16 属性找不到,而 2、6、10、14 存在?
- 10 和 11 或 14 和 15 有什么区别?
- 我在 1 中实际做了什么?
- 我在哪里声明属性 2、6、10、14?
- 为什么我可以在 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 永远不是只读的。 readonly
和 readwrite
选项用于方法声明。 (见上文 A.-C.)
2、6中直接设置了ivar。不能只读,所以一切都很好
在3、7你发送消息给一个不存在的方法,因为属性是只读的。