Objective-C is the programming language of choice for iOS an Mac OSX programming, so becoming proficient in native programming for those platforms is essential to building great applications. Apple emphasizes that programmers must understand the “retain-release” model of how to manage objects. True, but this is not enough. Unfortunately, Objective-C makes it exceedingly easy to inadvertently write code that breaks the retain-release model, leading bugs that cause programs to crash. You can adopt some practices to avoid such problems.
Objective-C Instance Variables (ivars) vs. Properties
[sourcecode language=”objc” light=”true”]
@interface MyClass
{
NSObject *objValue;
}
@end
[/sourcecode]
Within a method of the class, access to instance variables follow normal C/C++ syntax which allows them to be, simply, referenced by their name:
[sourcecode language=”objc” light=”true”]
self->objValue = obj;Â Â Â // Explicit reference to ivar; which is the same as
objValue = obj; // Implicitly assumed reference to ivar
[/sourcecode]
Objective-CÂ allows a “property” to correspond to a class’s instance variable:
[sourcecode language=”objc” light=”true”]
@interface MyClass
{
NSObject *objValue;
}
@property (retain) NSObject *objValue;
@end
@implementation MyClass
@synthesize objValue;
@end
[/sourcecode]
[sourcecode language=”objc” light=”true”]
self.objValue = obj;
[/sourcecode]
[sourcecode language=”objc” light=”true”]
objValue = obj;        // Use the ivar—does not use property’s setter
[/sourcecode]
Since it is so easy to refer to the instance variable rather than the desired property, use distinct names for each ivar and its property to avoid inadvertent references to the ivar:
[sourcecode language=”objc” light=”true”]
@interface MyClass
{
NSObject *_objValue; Â Â Â Â // Name, distinct from its property’s name
}
@property NSObject *objValue;Â Â // Common, property name
@end
@implementation MyClass
@synthesize objValue=_objValue; // Associate the ivar w/property
@end
[/sourcecode]
[sourcecode language=”objc” light=”true”]
objValue = obj; Â // !!! Yields a compiler error since there’s no ivar of that name.
[/sourcecode]
Apple ought to get off their “superset of C” horse since they’ve made so many changes already and change the meaning of unadorned symbol names to default to the property, if it is declared. This would break some code, but they already make a distinction between C/C++, Object-C, and mixed C/C++ and Object-C source files, this should be included in that distinction.
Example: Prove the Difference Between ivars and Properties
[sourcecode language=”objc” light=”true”]
@interface MyClass : NSObject
{
// Declare instance variables
NSObject *objValue;Â Â Â Â // This can be any kind of NSObject
NSObject *objValue2;Â Â Â // Second object
NSObject *objValue3;Â Â Â // Third object
}
// Declare properties
@property (retain) NSObject *objValue;
@property (retain) NSObject *objValue2;
@property (retain) NSObject *objValue3;
@property (retain) NSObject *objValue4;
– (void)testSymbolAccess; // Method to test this out
@end
@implementation MyClass
// Create accessor methods for properties (not necessary for Xcode 4 w/LLVM
@synthesize objValue;
@synthesize objValue2;
@synthesize objValue3;
@synthesize objValue4;Â Â Â // No explicit ivar (created by Xcode 4 automatically)
– (void)testSymbolAccess
{
NSObject *obj = [[NSObject alloc] init];Â Â Â Â Â Â Â // Implicit retain++ (i.e., 1)
// Add ref counts to avoid the count == 0 for the following sequence of tests.
[obj retain]; [obj retain]; // retain += 2 (i.e., 3)
NSLog(@"1. Ref count %d\n",[obj retainCount]); // Baseline
// "Normal" property usage:
self.objValue = obj; Â Â Â Â // Invoke accessor: retain++ (implicit retention)
NSLog(@"2. Ref count %d\n",[obj retainCount]);
self.objValue = nil; Â Â Â Â // Invoke accessor: retain– (as it sets ref to nil)
NSLog(@"3. Ref count %d\n",[obj retainCount]);
self.objValue = obj; Â Â Â Â // Invoke accessor: retain++ (implicit retention)
NSLog(@"4. Ref count %d\n",[obj retainCount]);
// Explict ivar reference:
self->objValue2 = obj; Â Â Â // Direct access to ivar; ref count untouched
NSLog(@"5. Ref count %d\n",[obj retainCount]);
self->objValue2 = nil; Â Â Â // And resetting it does nothing.
NSLog(@"6. Ref count %d\n",[obj retainCount]);
// So what does a bare reference do?
objValue3 = obj; Â Â Â Â Â Â // Which does this refer to, ivar or property?
NSLog(@"7. Ref count %d\n",[obj retainCount]); // ivar! (ref count is unchanged)
self.objValue3 = nil; Â Â Â // Explicty remove ref via property (retain–)
NSLog(@"8. Ref count %d\n",[obj retainCount]); // The ref count is out of sync
self.objValue = nil; Â Â Â Â // Remove reference: retain–
NSLog(@"9. Ref count %d\n",[obj retainCount]); // We’re really out of sync!
// And starting w/Xcode 4.0, you needn’t specify an ivar
objValue4 = obj; Â Â Â Â Â Â // Which does this refer to, ivar or property?
NSLog(@"10. Ref count %d\n",[obj retainCount]); // ivar! (ref count is unchanged)
self.objValue4 = nil; Â Â Â // If, later I explctly clear property (retain–)
NSLog(@"11. Ref count %d\n",[obj retainCount]); // Ref count is super out of sync
}
@end
[/sourcecode]
Calling testSymbolAccess
[sourcecode language=”objc” light=”true”]
MyClass *test=[[MyClass alloc] init];
[test testSymbolAccess];
[/sourcecode]
yields:
2011-11-25 17:50:45.006 [8135:207] 1. Ref count 3 2011-11-25 17:50:45.008 [8135:207] 2. Ref count 4 2011-11-25 17:50:45.008 [8135:207] 3. Ref count 3 2011-11-25 17:50:45.008 [8135:207] 4. Ref count 4 2011-11-25 17:50:45.009 [8135:207] 5. Ref count 4 2011-11-25 17:50:45.010 [8135:207] 6. Ref count 4 2011-11-25 17:50:45.010 [8135:207] 7. Ref count 4 2011-11-25 17:50:45.011 [8135:207] 8. Ref count 3 2011-11-25 17:50:54.558 [8135:207] 9b. Ref count 2 2011-11-25 17:51:35.019 [8135:207] 10. Ref count 2 2011-11-25 17:51:41.753 [8135:207] 11. Ref count 1