Must every ivar be a property?

Objective CIosPropertiesInstance Variables

Objective C Problem Overview


I see it recommended all over the place when coding for iOS that properties should be used for accessing instance variables because of the benefits this lends to memory management, among other things.

This advice doesn't sit terribly well with me. I find that using properties instead of plain old ivars just takes too much code and I don't really see the benefits if you're comfortable with memory management. Is it really that important? What's your approach to managing instance variables?

Objective C Solutions


Solution 1 - Objective C

It's not really necessary to declare properties for all ivars. A few points come to mind:

  • If an ivar is only going to be assigned to once during the lifetime of the object, you don't really gain anything by declaring a property. Just retain/copy/assign during init and then release as necessary during dealloc.
  • If an ivar is going to be changed frequently, declaring a property and always using the accessors will make it easier to avoid memory management errors.
  • You can declare properties in a class extension in the .m file rather than the .h file if the properties and ivars are meant to be private.
  • When targeting iOS 4.0+, you don't need to declare ivars at all in your header if you define a property and synthesize accessors.

So I generally use properties, but for things like a NSMutableArray that an object allocates during init and uses to hold a bunch of whatevers, I'll use a plain old ivar since I'll never be reassigning the ivar.

Solution 2 - Objective C

While Daniel's answer is correct, I think it misses an important point. Namely:

> I find that using properties instead > of plain old ivars just takes too much > code and I don't really see the > benefits if you're comfortable with > memory management.

The benefits are consistency; consistent memory management and consistent behavior.

Notably, these two lines of code can actually have extremely different behavior at runtime:

iVar = [foo retain];
self.iVar = foo;

The first is a direct setting of the instance variable and there will be no change notifications. The second goes through the setter and, thus, preserves any subclass customizations upon set and ensures that any observers of the property are notified of the change.

If you are using ivars directly throughout your code (internally to the class -- if you are using ivars of an instance directly from outside that instance, well... any contractor working on your codebase should double their rates ;), then you must either also handle change notification propagation manually (typically by calling willChangeValueForKey:/didChangeValueForKey) or explicitly engineer your application to avoid use of mechanisms that rely upon Key-Value Observation.

You say "takes too much code". I don't see that; in the above two lines of code, the dot syntax is fewer characters. Even calling the setter method using traditional syntax would be less code.

And do not discount the value in centralizing memory management; one accidental omission in a myriad of call sites and crash city.

Solution 3 - Objective C

Property are just syntax sugar that avoid you to write same methods over and over. With a property you have a setter that release the old object and retain the new one for free.

Solution 4 - Objective C

For the private fields - I suggest it is safe to use direct ivars only for primitive types (BOOL/int/float etc). I find a good practice wrapping everything related to memory-management in properties - even rarely-used fields. Additional bonus of this approach is that IDE usually highlights direct ivars access differently, so you always have a nice separation of simple scalar fields and object-type fields.

Contrary to this I would strongly discourage any direct ivars in the class public interface. Because of the dynamic nature of language it can lead to runtime errors that are extremely hard to find, localize and fix. Consider the following hierarchy

@interface BaseControl
...
@end

@interface Label : BaseControl
...
@end

@interface Button : BaseControl {
  @public
    BOOL enabled;
}
@end

and a code snippet

- (void)enableAllButtons {
    NSArray *buttons = [self getAllButtons];   // expected to contain only Button instances
    for (Button *button in buttons) {
        button->enabled = YES;
    }
} 

Now imagine there's an error somewhere in -getAllButtons logic and you also get some Label's returned in that array - so those Label class instances will get missing ivar assigned. The fact that may be surprising is that -enableAllButtons will not crash in that case. But at that point those Label instances internal structure is corrupted and this will cause undefined behavior and crashes when they are used elsewhere.

Like some popular problems with memory management (and in general - with dangling pointers) - this kind of problems is hard to find and localize - because the appearance of the error usually is distant (in terms of time, code or app flow) from the place, causing the error. But with that particular problem you even don't have handy tools (like leak/zombies analyzers etc.) to help you localize and fix it - even when you learn how to reproduce it and can easily investigate erroneous state.

Obviously if you use @property (assign) BOOL enabled; you'll get an easy-to diagnose and fix runtime exception in -enableAllButtons.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDiegoView Question on Stackoverflow
Solution 1 - Objective CDaniel DickisonView Answer on Stackoverflow
Solution 2 - Objective CbbumView Answer on Stackoverflow
Solution 3 - Objective CSimone D'AmicoView Answer on Stackoverflow
Solution 4 - Objective CMax OView Answer on Stackoverflow