Objective-C 对象只是在堆上分配的 C 结构(嗯,或多或少)。当您声明一个实例变量 (ivar) 时,它被定义为该结构的偏移量。因此,如果您手动声明了一些这样的 ivars(不要再这样做了,但它说明了这一点):
@interface Foo : NSObject {
NSString *ivar1;
NSString *ivar2;
}
然后,当您+alloc
创建一个新实例(调用它foo
)时,该结构将是一些标头,后跟 NSObject 的 ivars,然后是 memory for,ivar1
然后是 memory for ivar2
。ivar1
将foo
点加上一些偏移量。(这不再完全正确,但请继续关注我;理解旧的实现更简单。)
由于foo
是指向结构的指针,因此您实际上可以直接将此偏移指针引用为foo->ivar1
. 它确实是一个结构。永远不要这样做,但这是合法的语法。
在@implementation
块内部,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 个访问器方法。它们几乎完全相同。搞砸的机会很多。而且很乏味。谢天谢地Accessorizer。
使用 ObjC2,如果您添加了@synthesize
. 它会自动创建一个与属性同名的 ivar,并编写一个 getter 和(如果需要)setter 来读取和写入该 ivar。传递=_property
只是更改使用的 ivar 的名称。我们称之为“支持 ivar”。
现在,在最新版本的编译器中,您甚至不需要@synthesize
. 这种模式非常普遍,并且已经存在了几十年,现在它是默认的,除非你告诉编译器不要这样做。它会自动合成带有前导下划线的 ivar(这是最佳实践)。
您应该知道的另一条信息是,您应该始终使用访问器来访问 ivar,即使在对象内部也是如此。唯一的例外是init
anddealloc
方法。在那里您应该直接访问 ivar(使用前导下划线)。