“property is backed by an ivar” ? What technically does that mean?

自闭症网瘾萝莉.ら 提交于 2019-12-03 16:31:19
Rob Napier

An Objective-C object is just a C struct that is allocated on the heap (well, more or less). When you declare an instance variable (ivar), it is defined as an offset into that struct. So if you manually declared some ivars like this (don't do it this way anymore, but it illustrates the point):

@interface Foo : NSObject {
   NSString *ivar1;
   NSString *ivar2;
}

Then when you +alloc a new instance (call it foo), the struct will be some header followed by the ivars of NSObject followed by memory for ivar1 followed by memory for ivar2. ivar1 will be the foo point plus some offset. (This isn't exactly true anymore, but stay with me; it's simpler to understand the old implementation.)

Since foo is a pointer to a struct, you can actually refer directly to this offset pointer as foo->ivar1. It really is a struct. Never do this, but it is legal syntax.

Inside of the @implementation block, ivar1 is automatically translated to self->ivar1. Don't worry too much about how self is implemented, but trust that it's a pointer to your struct. Again, never use this -> syntax. It's an underlying implementation detail (and isn't always possible anymore; see below).

OK, so that's what an ivar is. In the old days (ObjC 1.0), that's actually all we had. You declared your ivars, and then you hand-created accessor methods that would set and return their values.

Then ObjC2 comes along, which in some cases also gave us something called the non-fragile ABI. That changes the underlying implementation of ivars somewhat, so you can't always actually use -> anymore. But you shouldn't have been using it anyway. Even so, it's simpler to pretend things are the old way. More to the point, ObjC2 added this new thing called "properties." A property is just a promise to implement certain methods. So when you say:

@property (nonatomic, readwrite, strong) NSString *property;

this is almost identical to saying the following:

- (NSString *)property;
- (void)setProperty:(NSString *)aProperty;

(The difference is very seldom important.) Note that this doesn't provide an implementation. It doesn't create ivars. It just declares some methods.

Now in ObjC1, we wrote the same accessor code over and over and over again. You had 20 writable ivars, you wrote 40 accessor methods. And they were almost identical. Lots of opportunities to mess up. And a lot of tedium. Thank goodness for Accessorizer.

With ObjC2, the compiler would give you the most common implementation for free if you added @synthesize. It would automatically make an ivar with the same name as the property, and write a getter and (if needed) setter to read and write that ivar. Passing =_property just changes the name of the ivar used. We call this the "backing ivar."

Now, in the latest version of the compiler, you don't even need @synthesize. This pattern is so insanely common, and has been for decades, that it is now the default unless you tell the compiler not to do it. And it automatically synthesizes an ivar with a leading underscore (which is best practice).

The one other piece of information you should know is that you should always use the accessor to access the ivar, even inside of the object. The only exceptions are in the init and dealloc methods. There you should directly access the ivar (using the leading underscore).

Just to be clear, when you do @synthesize myProperty = _myIvarPropertyNameToUse;, your only changing the name of the backing ivar. The line @synthesize myProperty; would also create a backing ivar, but it would be called myProperty, instead of _myIvarPropertyNameToUse...

The backing ivar is part of the object, so yes it's on the heap. It can be used as a true variable, meaning you can get and set it in the object code.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!