Should I use ivars in Objective-C?
Asked Answered
D

4

7

I have an application that I'm writing that uses solely @properties. I have not one ivar declared at all in any of my class files. As I understand it ivars are no longer needed with the introduction of @property. Am I coding according to best practice? Will this end up biting me in the proverbial butt in the long term? I have been reading mixed reviews on what is "right" and "wrong"...

Dwarfism answered 25/1, 2012 at 21:34 Comment(2)
Note that if you have both an ivar declaration and a property declaration for the same ivar, your code isn't DRY (don't repeat yourself).Boudicca
The answer to this question depends, among other things, on when you asked the question. The "ideal" approach here has been a moving target.Yun
N
11

I generally don't declare ivars, either. I will often use @synthesize foo = foo_; though to prevent direct access when I meant through-method or vice-versa. And I always let the compiler automatically synthesize the ivar with the _ prefix (which prevents accidental direct access, as per the struck phrase).

And, as Caleb said, there are still ivars floating about, you just don't explicitly declare 'em unless you really want to (which, really, you don't as exposed ivars in the headers are not useful to clients of the class, if your API is designed appropriately).

I also find that the hype over "only use direct access in init/dealloc, use setter/getter everywhere else" to be largely overblown and, thus, just use the setter/getter everywhere. The reality is that if you have observers during initialization/deallocation, you are already hosed; the state of the object is, by definition, undefined during construction/destruction and, thus, an observer can't possibly reason correctly about the state.


As Caleb points out, another reason to use direct ivar access in init/dealloc is to avoid subclasses that implement custom setter/getter logic that may barf due to the undefined state of the object during init/dealloc.

While this may be true, I consider it a nasty architectural flaw to implement setters/getters with custom behavior. Doing so is fragile and makes it significantly more difficult to refactor the code over time. As well, such custom behavior will often have dependency on other state within the object and that dependency then leads to order dependencies on state changes that are not at all reflected by the seeming simple @property declaration.

I.e. if your setters and getters are written such that foo.bar = bad; cannot be executed at any time on foo, then your code is busted.

Nescience answered 25/1, 2012 at 21:40 Comment(19)
I find that if I don't explicitly declare ivars, I can't inspect their values in the debugger. So I've taken the habit of explicitly declaring ivars for value types.Paries
@ohhorob Where do they state that? It's not that I disagree -- I've always thought that was the case. But I've searched for a statement to that effect in the docs a few times recently and came up empty. If you happen to know where it is, please share!Jillian
@Emile - Ahhh! That is great to know. I've always wondered why in some applications I can see values in the debugger and sometimes not. Perhaps this IS tied to whether or not an ivar is declared. I must investigate this immediately! =)Dwarfism
@Nescience - I think you have it backwards. I believe ohhorob is promoting the use of trailing and not the use of leading... (or perhaps you have a typo, or he has it backwards). PS -- Thanks all for your very informative answers!Dwarfism
@Nescience I thought the concern about init/dealloc was mostly due the possibility that a subclass might override an accessor and add side effects that could cause problems in those methods. More often than not I'm creating the subclass, not the thing to be subclassed, so it's not a big issue, but I'd appreciate your opinion.Jillian
Nope; given that my day job is an Apple engineer, I use _foo. :) @Jillian that is also correct... let me expand my answer.Nescience
@Nescience Nice update. Setters seem like attractive override points (even if they're really not): "I want to do X every time foo changes, so I'll override setFoo:." Maybe KVO is a better way to do that.Jillian
@caleb KVO is a decoupling improvement, but still leaves order dependency issues. Think of an object with "firstName" and "lastName"; observing both will lead to situations where you'll end processing a change where the firstName and lastName might not be in a coherent, matching, state. (I.e. if I change my name from "William Bumgarner" to "Bill Bumblaster" [from when Fake SJ was funny], it takes two transactions and processing after the first would be incorrect... but what if I only change my first name?)Nescience
@Jillian my answer to a previous question summarises it (#4581184). And it provides a link to the relevant section of the Apple Developer Library Coding Conventions document.Extravascular
One reason for preferring ivar_ over _ivar is that KVC gives direct access to the latter, however 'private'. Not very likely to bite often, admittedly.Romano
@bbum: one motivation for not using accessors in -init might be to be really clear about the semantics of an immutable object; ie. declare properties readonly (in a class extension), and assign using ivars in init (and only init).Romano
@Cris both good points; though I would claim that any use of valueForKey: and setValue:forKey: in code is generally a sign of a Design Gone Wrong. As for readonly properties, I often redeclare them as read write in a class extension at the top of my .m for internal use by my class. Especially when not using ARC, this guarantees all memory management is handled automatically through the setter/getter.Nescience
@bbum: have you written more about not using KVC accessors anywhere? Curious, but don't want to derail comments here (and design-related questions don't tend to go down well with the SO cops).Romano
I haven't, but -- in short -- use of valueForKey: bypasses any compiler checks. Given that you have to write the code, you should just write the call. Certainly, you may be computing a key on the fly, but -- even then -- it is best to avoid or, at the least, isolate the calls behind a sensible API that does a bunch of validation internally.Nescience
@bbum: right, I can see the sense in that. I have a specific case in mind for which KVC's ability to return a mutable proxy collection is a godsend; but I'm making those calls only on a class written specifically for that purpose.Romano
circling back on my question after a few months... I use @property for everything now, with @synthesize foo = foo_;, no iVars anywhere. I also don't like using the dot-notation anymore, I use the setter for everything... Thanks all for your comments!Dwarfism
To be clear; using foo.bar = 42 vs. [foo setBar:42] is entirely a personal choice of style. There is no difference in the generated code. To each his own (I use . pretty much all the time with @property declared stuff and pretty much never -- sometimes anArray.count or aString.length -- otherwise).Nescience
@Nescience do you now use autosynthesis, which infers @synthesize foo = _foo, instead of @synthesize foo = foo_?Pelerine
@MattDiPasquale Thanks! Updated! Yup -- I always use automatic synthesis. Fewer lines of code, the better.Nescience
J
15

It's not so much that instance variables aren't needed. It's just that instance variable declarations aren't needed. Given a property and a @synthesize statement, the compiler will take care of creating the instance variable along with appropriate accessor methods.

There's nothing wrong with using properties exclusively. They simplify memory management. There's also nothing wrong with using iVars without properties, if that's what you want. If you want to use properties but don't want to advertise the accessors to the rest of the world (i.e. maintain encapsulation), consider declaring your non-public properties in a class extension (basically an anonymous category in your implementation file).

Jillian answered 25/1, 2012 at 21:39 Comment(0)
N
11

I generally don't declare ivars, either. I will often use @synthesize foo = foo_; though to prevent direct access when I meant through-method or vice-versa. And I always let the compiler automatically synthesize the ivar with the _ prefix (which prevents accidental direct access, as per the struck phrase).

And, as Caleb said, there are still ivars floating about, you just don't explicitly declare 'em unless you really want to (which, really, you don't as exposed ivars in the headers are not useful to clients of the class, if your API is designed appropriately).

I also find that the hype over "only use direct access in init/dealloc, use setter/getter everywhere else" to be largely overblown and, thus, just use the setter/getter everywhere. The reality is that if you have observers during initialization/deallocation, you are already hosed; the state of the object is, by definition, undefined during construction/destruction and, thus, an observer can't possibly reason correctly about the state.


As Caleb points out, another reason to use direct ivar access in init/dealloc is to avoid subclasses that implement custom setter/getter logic that may barf due to the undefined state of the object during init/dealloc.

While this may be true, I consider it a nasty architectural flaw to implement setters/getters with custom behavior. Doing so is fragile and makes it significantly more difficult to refactor the code over time. As well, such custom behavior will often have dependency on other state within the object and that dependency then leads to order dependencies on state changes that are not at all reflected by the seeming simple @property declaration.

I.e. if your setters and getters are written such that foo.bar = bad; cannot be executed at any time on foo, then your code is busted.

Nescience answered 25/1, 2012 at 21:40 Comment(19)
I find that if I don't explicitly declare ivars, I can't inspect their values in the debugger. So I've taken the habit of explicitly declaring ivars for value types.Paries
@ohhorob Where do they state that? It's not that I disagree -- I've always thought that was the case. But I've searched for a statement to that effect in the docs a few times recently and came up empty. If you happen to know where it is, please share!Jillian
@Emile - Ahhh! That is great to know. I've always wondered why in some applications I can see values in the debugger and sometimes not. Perhaps this IS tied to whether or not an ivar is declared. I must investigate this immediately! =)Dwarfism
@Nescience - I think you have it backwards. I believe ohhorob is promoting the use of trailing and not the use of leading... (or perhaps you have a typo, or he has it backwards). PS -- Thanks all for your very informative answers!Dwarfism
@Nescience I thought the concern about init/dealloc was mostly due the possibility that a subclass might override an accessor and add side effects that could cause problems in those methods. More often than not I'm creating the subclass, not the thing to be subclassed, so it's not a big issue, but I'd appreciate your opinion.Jillian
Nope; given that my day job is an Apple engineer, I use _foo. :) @Jillian that is also correct... let me expand my answer.Nescience
@Nescience Nice update. Setters seem like attractive override points (even if they're really not): "I want to do X every time foo changes, so I'll override setFoo:." Maybe KVO is a better way to do that.Jillian
@caleb KVO is a decoupling improvement, but still leaves order dependency issues. Think of an object with "firstName" and "lastName"; observing both will lead to situations where you'll end processing a change where the firstName and lastName might not be in a coherent, matching, state. (I.e. if I change my name from "William Bumgarner" to "Bill Bumblaster" [from when Fake SJ was funny], it takes two transactions and processing after the first would be incorrect... but what if I only change my first name?)Nescience
@Jillian my answer to a previous question summarises it (#4581184). And it provides a link to the relevant section of the Apple Developer Library Coding Conventions document.Extravascular
One reason for preferring ivar_ over _ivar is that KVC gives direct access to the latter, however 'private'. Not very likely to bite often, admittedly.Romano
@bbum: one motivation for not using accessors in -init might be to be really clear about the semantics of an immutable object; ie. declare properties readonly (in a class extension), and assign using ivars in init (and only init).Romano
@Cris both good points; though I would claim that any use of valueForKey: and setValue:forKey: in code is generally a sign of a Design Gone Wrong. As for readonly properties, I often redeclare them as read write in a class extension at the top of my .m for internal use by my class. Especially when not using ARC, this guarantees all memory management is handled automatically through the setter/getter.Nescience
@bbum: have you written more about not using KVC accessors anywhere? Curious, but don't want to derail comments here (and design-related questions don't tend to go down well with the SO cops).Romano
I haven't, but -- in short -- use of valueForKey: bypasses any compiler checks. Given that you have to write the code, you should just write the call. Certainly, you may be computing a key on the fly, but -- even then -- it is best to avoid or, at the least, isolate the calls behind a sensible API that does a bunch of validation internally.Nescience
@bbum: right, I can see the sense in that. I have a specific case in mind for which KVC's ability to return a mutable proxy collection is a godsend; but I'm making those calls only on a class written specifically for that purpose.Romano
circling back on my question after a few months... I use @property for everything now, with @synthesize foo = foo_;, no iVars anywhere. I also don't like using the dot-notation anymore, I use the setter for everything... Thanks all for your comments!Dwarfism
To be clear; using foo.bar = 42 vs. [foo setBar:42] is entirely a personal choice of style. There is no difference in the generated code. To each his own (I use . pretty much all the time with @property declared stuff and pretty much never -- sometimes anArray.count or aString.length -- otherwise).Nescience
@Nescience do you now use autosynthesis, which infers @synthesize foo = _foo, instead of @synthesize foo = foo_?Pelerine
@MattDiPasquale Thanks! Updated! Yup -- I always use automatic synthesis. Fewer lines of code, the better.Nescience
S
2

Using iVars certainly isn't wrong, however best practises now do push for using @property instead.

Sucy answered 25/1, 2012 at 21:38 Comment(0)
E
0

One place where you may want to use an ivar is when you want to declare a protected property. You declare the property in the .m file for a class, and declare its corresponding ivar in the .h with the @protected directive. This will then let you have a protected access in the subclass. There's no alternative for protected access to members.

Engineering answered 19/12, 2013 at 18:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.