Objective-C 对象只是在堆上分配的 C 结构体(或多或少)。当您声明实例变量 (ivar) 时,它被定义为该结构的偏移量。因此,如果您像这样手动声明一些 ivars(不再这样做,但它说明了这一点):
@interface Foo : NSObject {
NSString *ivar1;
NSString *ivar2;
}
然后当你+alloc
一个新实例(称之为foo
),该结构将是一些标头,后面是 NSObject 的 ivars,后面是内存ivar1
接下来是记忆ivar2
. ivar1
将是foo
点加上一些偏移量。 (这不再完全正确,但请听我说;更容易理解旧的实现。)
Since foo
是一个指向结构体的指针,你实际上可以直接引用这个偏移指针:foo->ivar1
。它确实是一个结构。永远不要这样做,但这是合法的语法。
里面的@implementation
block, ivar1
会自动翻译为self->ivar1
。不用太担心如何self
已实现,但请相信它是指向您的结构的指针。再次强调,永远不要使用这个->
句法。这是一个底层的实现细节(并且不再总是可能的;见下文)。
好的,这就是 ivar。在过去(ObjC 1.0),这实际上就是我们所拥有的一切。您声明了 ivars,然后手动创建了可设置并返回其值的访问器方法。
然后 ObjC2 出现了,在某些情况下它还为我们提供了一种称为非脆弱 ABI 的东西。这在某种程度上改变了 ivars 的底层实现,所以你不能总是实际使用->
不再了。但无论如何你都不应该使用它。即便如此,假装事情还是老样子还是比较简单。更重要的是,ObjC2 添加了一个称为“属性”的新东西。属性只是实现某些方法的承诺。所以当你说:
@property (nonatomic, readwrite, strong) NSString *property;
这几乎与下面的说法相同:
- (NSString *)property;
- (void)setProperty:(NSString *)aProperty;
(差异很少很重要。)请注意,这不提供实现。它不会创建 ivars。它只是声明了一些方法。
现在在 ObjC1 中,我们一遍又一遍地编写相同的访问器代码。您有 20 个可写的 ivars,编写了 40 个访问器方法。而且它们几乎一模一样。有很多搞砸的机会。还有很多乏味的事情。感谢上帝配饰 http://www.kevincallahan.org/software/accessorizer.html.
使用 ObjC2,如果您添加了,编译器将免费为您提供最常见的实现@synthesize
。它会自动创建一个与属性同名的 ivar,并编写一个 getter 和(如果需要)setter 来读取和写入该 ivar。通过=_property
只是更改所使用的 ivar 的名称。我们称之为“支持 ivar”。
现在,在最新版本的编译器中,您甚至不需要@synthesize
。这种模式非常常见,并且已经存在了几十年,以至于现在它已成为默认模式,除非您告诉编译器不要这样做。它会自动合成带有前导下划线的 ivar(这是最佳实践)。
您应该了解的另一条信息是,您应该始终使用访问器来访问 ivar,即使是在对象内部。唯一的例外是init
and dealloc
方法。在那里您应该直接访问 ivar(使用前导下划线)。