我想非常理解,但到目前为止,我并没有在一个地方考虑所有可能性而找到复杂的答案。我知道在现代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"];
问题是:
答案 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
合成了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永远不会读。 readonly
和readwrite
选项用于方法声明。 (见上文A. - C.)
在2,6中你直接设置了ivar。它不能只读,所以一切都很好。
在3,7中,您向不存在的方法发送消息,因为属性是只读的。