Vincent Gable’s Blog

August 19, 2010

The Most Useful Objective-C Code I’ve Ever Written

Actually, it’s the most useful code I’ve extended; credit for the core idea goes to Dave Dribin with his Handy NSString Conversion Macro.

LOG_EXPR(x) is a macro that prints out x, no matter what type x is, without having to worry about format-strings (and related crashes from eg. printing a C-string the same way as an NSString). It works on Mac OS X and iOS. Here are some examples,

LOG_EXPR(self.window.screen);

self.window.screen = <UIScreen: 0x6d20780; bounds = {{0, 0}, {320, 480}}; mode = <UIScreenMode: 0x6d20c50; size = 320.000000 x 480.000000>>

LOG_EXPR(self.tabBarController.viewControllers);

self.tabBarController.viewControllers = (
“<UINavigationController: 0xcd02e00>”,
“<SavingsViewController: 0xcd05c40>”,
“<SettingsViewController: 0xcd05e90>”
)

Pretty straightforward, really. The biggest convenience so far is having the expression printed out, so you don’t have to write out a name redundantly in the format string (eg. NSLog(@"actionURL = %@", actionURL)). But LOG_EXPR really shows it’s worth when you start using scalar or struct expressions:

LOG_EXPR(self.window.windowLevel);

self.window.windowLevel = 0.000000

LOG_EXPR(self.window.frame.size);

self.window.frame.size = {320, 480}

Yes, there are expressions that won’t work, but they’re pretty rare for me. I use LOG_EXPR every day. Several times. It’s not quite as good as having a REPL for Cocoa, but it’s handy.

Give it a try.

How It Works

The problem is how to pick a function or format string to print x, based on the type of x. C++’s type-based dispatch would be a good fit here, but it’s verbose (a full function-definition per type) and I wanted to use pure Objective-C if possible. Fortunately, Objective-C has an @encode() compiler directive that returns a string describing any type it’s given. Unfortunately it works on types, not variables, but with C99 the typeof() compiler directive lets us get the type of any variable, which we can pass to @encode(). The final bit of compiler magic is using stringification (#) to print out the literal string inside LOG_EXPR()‘s parenthesis.

The Macro, Line By Line

1 #define LOG_EXPR(_X_) do{\
2 	__typeof__(_X_) _Y_ = (_X_);\
3 	const char * _TYPE_CODE_ = @encode(__typeof__(_X_));\
4 	NSString *_STR_ = VTPG_DDToStringFromTypeAndValue(_TYPE_CODE_, &_Y_);\
5 	if(_STR_)\
6 		NSLog(@"%s = %@", #_X_, _STR_);\
7 	else\
8 		NSLog(@"Unknown _TYPE_CODE_: %s for expression %s in function %s, file %s, line %d", _TYPE_CODE_, #_X_, __func__, __FILE__, __LINE__);\
9 }while(0)
  1. The first and last lines are a way to put {}‘s around the macro to prevent unintended effects. The do{}while(0); “loop” does nothing else.
  2. First evaluate the expression, _X_, given to LOG_EXPR once, and store the result in a _Y_. We need to use typeof() (which had to be written __typeof__() to appease some versions of GCC) to figure out the type of _Y_.
  3. _TYPE_CODE_ is c-string that describes the type of the expression we want to print out.
  4. Now we have enough information to call a function, VTPG_DDToStringFromTypeAndValue() to convert the expression’s value to a string. We pass it the _TYPE_CODE_ string, and the address of _Y_, which is a pointer, and has a known size. We can’t pass _Y_ directly, because depending on what _X_ is, it will have different types and could be of any size.
  5. VTPG_DDToStringFromTypeAndValue() returns nil if it can’t figure out how to convert a value to a string.
  6. Everything went well, print the stringified expression, #_X_, and the string representing it’s value, _STR_.
  7. otherwise…
  8. The expression had a type we can’t handle, print out a verbose diagnostic message.
  9. See line 1.

The VTPG_DDToStringFromTypeAndValue() Function

See the source in VTPG_Common.m:

It’s derived from Dave Dribin‘s function DDToStringFromTypeAndValue(), and is pretty straightforward: strcmp() the type-string, and if it matches a known type call a function, or use +[NSString stringWithFormat]:, to turn the value into a string.

The First Step Twords Fixing Your Macro Problem is Admitting it…

So yeah, maybe I went a little wild with macros here…

But it took out some WET-ness of the original code, and prevents me from accidentally mixing up types in a long wall of ifs, eg.

else if (strcmp(typeCode, @encode(NSRect)) == 0)
{
    return NSStringFromRect(*(NSRange *)value);
}
else if (strcmp(typeCode, @encode(NSRange)) == 0)
{
    return NSStringFromRect(*(NSRange *)value);
}

If I were cool, I’d use NSDictionarys to map from the @encode-string to an appropriate format string or function pointer. This is conceptually cleaner; less error-prone than using macros; and almost certainly faster. Unfortunately, it gets a little tricky with functions, since I need to deference value into the proper type.

One final note from my testing, I could do away with the strcmp()s, because directly comparing @encode string pointers (eg if(typeCode == @encode(NSString*)) works. I don’t know if it will always work though, so relying on it strikes me as a profoundly Bad Idea. But maybe that bad idea will give someone a good idea.

Limitations

Arrays

C arrays generally muck things up. Casting to a pointer works around this:

char x[14] = "Hello, world!";
//LOG_EXPR(x); //error: invalid initializer
LOG_EXPR((char*)x); //prints fine

__func__

Because it is a static const char [], __func__ (and __FUNCTION__ or __PRETTY_FUNCTION__) need casting to char* to work with LOG_EXPR. Because logging out a function/method call is something I do frequently, I use the macro:

#define LOG_FUNCTION()	NSLog(@"%s", __func__)

long double (Leopard and older)

On older systems, LOG_EXPR won’t work with a long double value, because @encode(long double) gives the same result as @encode(double). This is a known issue with the runtime. The top-level LOG_EXPR macro could detect a long double with if((sizeof(_X_) == sizeof(long double)) && (_TYPE_CODE_ == @encode(double))). But I doubt this will ever be necessary.

I haven’t actually written any code that uses long double, because I use NSDecimal, or another base-10 number format, for situations that require more precision than a double.

Scaling and Frameworks

Growing LOG_EXPR to handle every type is a lot of work. I’ve only added types that I’ve actually needed to print. This has kept the code manageable, and seems to be working so far.

The biggest problem I have is how to deal with types that are in frameworks that not every project includes. Projects that use CoreLocation.framework need to be able to use LOG_EXPR to print out CoreLocation specific structs, like CLLocationCoordinate2D. But projects that don’t use CoreLocation.framework don’t have a definition of the CLLocationCoordinate2D type, so code to convert it to a string won’t compile. There are two ways I’ve tried to solve the problem

Comment-out framework-specific code

This is pretty self-explanatory, I’ll fork VTPG_Common.m and un-comment-out code for types that my project needs to print. It works, but it’s drudgery. Programmers hate that.

Hardcode type info

The idea is to hard-code the string that @encode(SomeType) would evaluate to, and then (since we know how SomeType is laid out in memory) use casting and pointer-arithmetic to get at the fields.

For example:

//This is a hack to print out CLLocationCoordinate2D, without needing to #import <CoreLocation/CoreLocation.h>
//A CLLocationCoordinate2D is a struct made up of 2 doubles.
//We detect it by hard-coding the result of @encode(CLLocationCoordinate2D).
//We get at the fields by treating it like an array of doubles, which it is identical to in memory.
if(strcmp(typeCode, "{?=dd}")==0)//@encode(CLLocationCoordinate2D)
	return [NSString stringWithFormat:@"{latitude=%g,longitude=%g}",((double*)value)[0],((double*)value)[1]];

This Just Works in a project that includes CoreLocation, and doesn’t mess up projects that don’t. Unfortunately it’s horribly brittle. Any Xcode or system update could break it. It’s not a tenable fix.

Areas for Improvement

If there’s some type LOG_EXPR can’t handle that you need, please jump right in and improve it!

When I have time, I plan to write a general parser for @encode()-strings. This will let me print out any struct, which mostly solves the type-defined-in-missing-framework problem, and would let LOG_EXPR Just Work with types from all kinds of POSIX/C libraries.

Using LOG_EXPR() in Your Project

Download VTPG_Common.m and VTPG_Common.h from my github repository, and add them to your Xcode project.

Now just add the line #import "VTPG_Common.h" to your prefix file (named <ProjectName>_Prefix.pch by default), after the #ifdef __OBJC__, for example:

#ifdef __OBJC__
    #import <Foundation/Foundation.h>
    // maybe other files, depending on project  template...
    #import "VTPG_Common.h"
#endif

Now LOG_EXPR() will work everywhere in your project.

August 15, 2010

My UIViewController Template

Filed under: iPhone,Objective-C,Programming,Sample Code | , , ,
― Vincent Gable on August 15, 2010

I haven’t formalized this as a proper Xcode template, but this is what’s in my PrototypeViewController.m file that I copy/paste over the UIViewController subclasses Xcode makes.


@interface MyViewController ()
@end

@implementation MyViewController
- (void) releaseViewObjects;
{
	if([[self superclass] instancesRespondToSelector:@selector(releaseViewObjects)])
		[(id)super releaseViewObjects];
}

- (void)viewDidUnload;
{
    [super viewDidUnload];
    [self releaseViewObjects];
}

- (void)dealloc;
{
    [self releaseViewObjects];
    [super dealloc];
}

@end

Here are the reasons why, in no particular order:

Commented-Out Code is Evil

Littering source code with “comments” full of crufty, obsolete, or unimplemented code is not a good thing. Xcode’s default template is full of commented-out code. If you’re totally new to the platform, starting from the templates aren’t a bad way to learn. But in my experiance, they do harm to a production code-base, by injecting hundreds of lines of commented-out code into a project.

Share code between viewDidUnload and dealloc With releaseViewObjects

In my world, releaseViewObjects is solely responsible for cleaning up every IBOutlet, and any objects created in viewDidLoad.

There are technical reasons why this is a little scary. Calling a method in dealloc is potentially risky, because the object may be in an invalid half-torn-down state, and because Dark Runtime Magicks could be afoot (see Using Accessors in Init and Dealloc.)

But in my experience, such bugs, although as scary as they sound, are rare corner-cases and still quite fixable. But every UIViewController needs to clean up after itself, so simplifying the universally common case is a net win.

The if([[self superclass] instancesRespondToSelector:@selector(releaseViewObjects)]) test wouldn’t be necessary if I added another class between UIViewController and my real code, so that I was sure my class’ super implemented releaseViewObjects. But adding a subclass just to implement one empty method, to avoid a two-line test, isn’t worth it.

The (id)super cast is intentional, to prevent compiler warnings.

I have to use the more complex [[self superclass] instancesRespondToSelector: test, because -[super respondsToSelector:] doesn’t work.

I Won’t Really Get To didReceiveMemoryWarning

I’m not proud to admit this, but it’s true. We’ve all been told that a good iPhone program must release resources when it gets a memory warning, or else it will be killed. But in practice, there have always been better places to spend my time (or at least it sure feels that way!) Spending a few hours in Instruments to fix leaks prevents memory warnings in the first place, and that’s a bigger win.

Besides, 80% of what didReceiveMemoryWarning would do is handled in releaseViewObjects, which is automatically called by the default implementation.

So I break with Xcode and leave didReceiveMemoryWarning out of my template, because the default class won’t use it.

What About init?

I don’t have a default init(With…) method. I try to use autorelease-ed objects everywhere I can, so I’m more comfortable implementing +[MyViewController viewControllerForFoo:].

But I don’t have a default constructor of any kind, because a constructor should take every value it needs, and I don’t know what these values are until I’ve written a bit more of the class. It’s a chicken and egg problem.

Once I’ve written out a bit more of the class, I’ll usually build something that looks like:

+ (RouteMapViewController*) routeMapViewControllerWithWaypoints:(NSArray*)waypoints mapRegion:(MKCoordinateRegion)region;
{
	RouteMapViewController *vc = [[[self class] new] autorelease];
	vc.title = NSLocalizedString(@"The Path",@"");
	vc.hidesBottomBarWhenPushed = YES;
	vc.waypoints = waypoints;
	vc.mapRegion = region;
	return vc;
}

For what it’s worth I use this pattern to implement a 0-argument -init.

Empty Class Extension

Class extensions are the best way to have “private” things in Objective-C. They let the compiler catch objects using another object’s private methods. They let a class have publicly readonly, but internally readwrite, properties.

Bottom line: every nontrivial object I’ve written uses them, so they’re in my template.

Nothing Else (For Now)

My template is smaller than Xcode’s. That is by design. Outside of esoteric contests, having less code to maintain is a good thing. So I prefer a template that tries very hard to avoid adding code I don’t need.

Do you disagree with any of my choices? Please leave a comment explaining why.

July 19, 2010

#define String

When I need a string-constant, I #define it, instead of doing the “right” thing and using an extern const NSString * variable.

UPDATE 2010-07-20

Thanks to Elfred Pagen for pointing out that you should always put () around your macros. Wrong: #define A_STRING @"hello"

instead use (), even when you don’t think you have to:

#define A_STRING (@"hello")

This prevents accidental string concatenation. In C, string-literals separated only by whitespace are implicitly concatenated. It’s the same with Objective-C string literals. This feature lets you break long strings up into several lines, so NSString *x = @"A long string!" can be rewritten:

NSString *x =
	@"A long"
	@" string!";

Unfortunately, this seldom-used feature can backfire in unexpected ways. Consider making an array of two strings:

#define X @"ex"
#define P @"plain"
a = [NSArray arrayWithObjects:X
                              P,
                              nil];

That looks right, but I forgot a “,” after X, so after string-concatenation, a is ['explain'], not ['ex','plain'].

Moral of the story: you can never have too many ()’s in macros.

And, now, back to why I use #define

It’s less code

Using an extern variable means declaring it in a header, and defining it in some implementation file. But a macro is just one line in a header.

It’s faster to lookup

Because there’s only the definition of a macro, Open Quickly/command-double-clicking a macro always jumps to the definition, so you can see what it’s value is in one step. Generally Xcode jumps to a symbol’s declaration first, and then it’s definition, making it slower to lookup the value of a const symbol.

It’s still type safe

An @"NSString literal" has type information, so mistakes like,

#define X (@"immutable string")
NSMutableString *y = X;
[y appendString:@"z"];

still generate warnings.

It lets the compiler check format-strings

Xcode can catch errors like “[NSString stringWithFormat:@"reading garbage since there's no argument: %s"]“, if you let it. Unfortunately, the Objective-C compiler isn’t smart enough to check [NSString stringWithFormat:externConstString,x,y,z]; because it doesn’t know what an extern variable contains until link-time. But preprocessor macros are evaluated early enough in the build process that that the compiler can check their values.

It can’t be changed at runtime

It’s possible to change the value of const variables through pointers, like so:

const NSString* const s = @"initial";
NSString **hack = &s;
*hack = @"changed!";
NSLog(s);//prints "changed!"

Yes this is pathological code, but I’ve seen it happen (I’m looking at you AddressBook.framework!)

Of course, you can re-#define a preprocessor-symbol, so macros aren’t a panacea for pathological constant-changing code. (Nothing is!) But they push the pathology into compile time, and common wisdom is that it’s easier to debug compile-time problems, so that’s a Good Thing. You may disagree there, and you may be right! All I can say for sure is that in my experience, I’ve had bugs from const values changing at runtime, but no bugs from re-#define-ed constants (yet).

Conclusion

Preprocessor macros are damnably dangerous in C. Generally you should avoid them. But for NSString* constants in applications, I think they’re easier, and arguably less error prone. So go ahead and #define YOUR_STRING_CONSTANTS (@"like this").

July 8, 2010

NSDictionary Copies It’s Keys

Filed under: Bug Bite,Cocoa,iPhone,MacOSX,Objective-C,Programming | , , ,
― Vincent Gable on July 8, 2010

An NSDictionary will retain it’s objects, and copy it’s keys.

Here are some effects this has had on code I’ve worked on.

  • Sometimes you get the same object you put in, sometimes not.
    Immutable objects are optimized to return themselves as a copy. (But with some exceptions!). So the following code:

    	NSDictionary *d = [NSDictionary dictionaryWithObject:@"object" forKey:originalKey];
    	for(id aKey in d)
    		if(aKey == originalKey)
    			NSLog(@"Found the original key!");
    

    Might print “Found the original key!”, and might not, depending on how [originalKey copy] is implemented. For this reason, never use pointer-equality when comparing keys.

  • Mutable objects make bad keys. If x is a mutable NSObject, [x copy] is an immutable copy of x, at that point in time. Any changes to x are not reflected in the copy. For example,
    	[dict setObject:x forKey:key];
    	//...code that changes key, but not dict
    	assert([[dict objectForKey:key] isEqual:x]); //fails!
    

    Because the copy is an immutable object, it will blow up if you try to mutate it.

    	NSMutableString *key = //something...
    	[dict setObject:x forKey:key];
    	for(NSMutableString *aKey in dict)
    		[aKey appendString:@"2"]; //Error, aKey isn't mutable, even though key is!
    		
    
  • View objects make bad keys. Views have state related to the screen: their frame, position in the view hierarchy, animation layers, etc. When you copy a view object, the copy won’t (always) be isEqual: to the original, because it’s not on the screen in exactly the same way.
  • Your classes must support NSCopying to be used as a key in an NSDictionary, you can’t just implement -hash and -isEqual: in your custom classes.

Of course, this isn’t a complete list of every way key-copying can trip you up. But if you understand what copy means in Cocoa, and remember how NSDictionary works, you’ll be able to avoid or quickly solve any issues.

How to Document Such Behavior Better Than Apple Did

Given what we know about NSDictionary, what’s wrong with the following snippit from NSDictionary.h?

@interface NSMutableDictionary : NSDictionary
- (void)setObject:(id)anObject forKey:(id)aKey;
@end

Answer: aKey needs to implement NSCopying, so it should be of type (id<NSCopying>) instead of type (id). That way, the header is self-documenting, and, if like most smart programmers, you’re using autocomplete to type out Cocoa’s long method names, the auto-completed template will be self-documenting too.

June 14, 2010

Ask F-Script!

Filed under: Cocoa,iPhone,MacOSX,Objective-C,Programming | , , , , , ,
― Vincent Gable on June 14, 2010

F-Script is an amazingly useful tool for answering quick API
questions, like “What happens if I pass in nil“. I use it several times a week. For verifying corner-cases, F-Script is faster than google, stackoverflow, or reading header files. Just type in a questionable expression and instantly see what happens.

There’s a good tutorial to get you started quickly. I’m not going to reproduce it here, so if any of these examples aren’t clear, go read it.

Example: NSMutableArray

Objective-C had historically poor support for exceptions, and the Foundation/Cocoa libraries are pretty inconsistent about using them. For example, trying to add nil to an array throws an exception, but trying to remove nil from an array has no effect. Here’s how I used F-Script to verify that,

> a := NSMutableArray array

> a addObject:nil
NSInvalidArgumentException: *** -[NSCFArray insertObject:atIndex:]: attempt to insert nil

> a addObject:'foo'

> a
NSCFArray {'foo'}

> a removeObject:nil

> a
NSCFArray {'foo'}

If you’re not impressed, I understand. Static text really can’t convey the power of an interactive console. Sure, the F-Script syntax is marginally more concise than writing the equivalent code in Objective-C, but not enough that it matters. What matters is the interactivity, I got my answer as soon as I hit return. No waiting on the compiler. No switching between the program and Xcode. Immediate feedback.

You might prefer to use python as a Cocoa console. That’s cool! I prefer F-Script because it’s closer to Objective-C, but any tool with a REPL console works. If you have a favorite, please leave a comment!

REPL consoles for exploring Objective-C on a Mac:

June 2, 2010

NSHomeDirectory() is a Bad Thing

Filed under: Announcement,Cocoa,iPhone,MacOSX,Objective-C,Programming | , ,
― Vincent Gable on June 2, 2010

Code that uses NSHomeDirectory() is probably doing The Wrong Thing. It’s not appropriate to clutter up the user’s home directory — internal application-data should be stored in the Application Support directory (or a temporary file if it’s transient). So I can’t think of a good reason to get the path to the user’s home directory. Every use of NSHomeDirectory() I’ve seen is spamming the home directory, or getting a subdirectory in a brittle way.

For sample code that gets a directory robustly, using NSSearchPathForDirectoriesInDomains(), see Finding or creating the application support directory.

Because NSHomeDirectory() encourages so many bad practices, it should be deprecated.

Disabling NSHomeDirectory() in Your Projects

Add the following macro to your prefix file:

#define NSHomeDirectory() NSHomeDirectory_IS_DISCOURAGED_USE_NSSearchPathForDirectoriesInDomains_TO_GET_A_SUBDIRECTORY_OF_HOME

Then any use of NSHomeDirectory() will give the compiler error:

error:
‘NSHomeDirectory_IS_DISCOURAGED_USE_NSSearchPathForDirectoriesInDomains_TO_GET_A_SUBDIRECTORY_OF_HOME’ undeclared (first use in this function)

Tell Me I’m Wrong

If you’ve seen a legitimate use of NSHomeDirectory() please leave a comment! Just because I can’t think of one doesn’t mean they don’t exist.

May 26, 2010

drain an NSAutoReleasePool Don’t release it

To clean up an NSAutoreleasePool, do [pool drain]; not [pool release];

In a garbage-collected environment, sending any object a release message is hardcoded by the runtime to do nothing (very quickly). So [pool release] won’t do anything. But [pool drain] will signal the garbage collector to cleanup, and works correctly (just like release) in a non-garbage-collected environment.

Why This Still Matters on an iPhone

The iPhone doesn’t have garbage collection today. That doesn’t mean it never will. RIM and Android both support some kind of garbage collection. I’m too grizzled an Apple developer to not future proof my code, because I’ve been effected by Apple making some major runtime changes (eg. switching between PowerPC, x86, x86_64, and ARM processors). Section 3.3.1 of the iPhone SDK agreement means Apple’s runtime is the only game in town. It pays to be sure your code always plays nicely with it.

Using drain also helps your code will play nice with Mac OS X. That gives you more options to re-use and monazite it. If you decide to go the open-route, it means more people will be able to use your code.

May 25, 2010

Write dealloc FIRST

Filed under: Bug Bite,Cocoa,Objective-C,Programming | , , ,
― Vincent Gable on May 25, 2010

As soon as you give a class a new instance variable (ivar), update the class’s dealloc method (and viewDidUnload, if the ivar is an IBOutlet) to clean up the ivar. Do this before you write the code using the new ivar. Here’s why:

Never Forget

You can’t forget to release an ivar, if the code that reaps it is in place before the code that creates it. Updating dealloc first means less memory leaks.

Even with an impossibly good testing protocol, that catches every memory leak, it’s faster to fix memory leaks before they happen than to track them down after the fact.

You Know More Than They Do

Sometimes there’s an important step that must be done when cleaning up an ivar. Maybe you need to set it’s delegate to nil, or unregister for a notification, or break a retain cycle. You know this when you setup the ivar. But your coworkers don’t know this a priori. When you checkin code that leaks or triggers an analyzer warning, they’ll want to fix it, and since they know less than you do about your code, they’re more likely to miss a crucial step. (Even if you work alone, remember Future You! In N weeks, Future You will have to deal with all the code Present You wrote today … and they’ll be in the same situation as any other co-worker, because they won’t remember everything Present You knows. )

May 24, 2010

Never Name a Variable “Index”

Filed under: Bug Bite,C++,Cocoa,Objective-C,Programming | ,
― Vincent Gable on May 24, 2010

Never name a variable index, especially in C.

Instead say what it indexes. For example, if it is used to index an array of Foo objects, call it fooArrayIndex, or currentFooIndex.

If the index variable is just used to enumerate over a collection of objects, (eg. for(int i = 0; i < arraySize; i++){…} ) then iterate smarter, using a simpler construct that doesn’t require declaring auxiliary variables. (Eg., in Objective-C use Fast Enumeration). It’s not always possible to do this, but it’s always a good idea to try.1

Why index is Especially Bad in C

The standard strings.h header declares a function named index, that finds the first occurrence of a charicter in a C-string. In practical terms every C program will have the index function declared everywhere.

But when a variable is declared with the name index it shadows the function — meaning the local variable named index takes over the name index, so the function can’t be called anymore:

char * world = index("Hello, World", 'W');
NSLog(@"'%s'", world);

Prints “‘World'”, but

int index = 0;
char * world = index("Hello, World", 'W');
NSLog(@"'%s'", world);

Won’t compile, because an int isn’t a function.

Obviously this is a problem for code that uses the index() function — but honestly modern code probably uses a safer, unicode-aware string parsing function instead. What’s given me the most trouble is that shadowing index makes the compiler give lots of bogus warnings, if you have the useful GCC_WARN_SHADOW warning turned on.

There are other good reasons as, specific to Objective-C, which Peter Hosey covers.

1If you really can’t think of a better name than “index”, I prefer the more terse i. It sucks, but at least it’s shorter. Brevity is a virtue.

May 19, 2010

N.A.R.C.

Filed under: Cocoa,iPhone,MacOSX,Objective-C,Programming,Tips | , , ,
― Vincent Gable on May 19, 2010

How to remember Cocoa memory management:

Think NARC: “New Alloc Retain Copy”. If you are not doing any of those things, you don’t need to release.

–Andiih on Stack Overflow

Personally, I like to immediately autorelease anything I NARC-ed, on the same line. For example:

Foo* pityTheFoo = [[[Foo alloc] init] autorelease];

Admittedly, this makes for some ugly, bracey, lines. But I think it’s worth it, because you never having to worry about calling release if you also…

Use a @property (or Setter) Instead of retain

In other words I would write an init method that looked like:

- (id) init
{
	self = [super init];
	if (self) {
		_ivar = [[Foo alloc] init];
	}
	return self;
}

as:

- (id) init
{
	self = [super init];
	if (self) {
		self._ivar = [[[Foo alloc] init] autorelease];
	}
	return self;
}

(Or [self setIvar:[[[Foo alloc] init] autorelease]]; if you are one of those folks who hate the dot-syntax.)

It’s debatable if using acessors in init and dealloc is a good idea. I even left a comment on that post arguing against it. But since then I’ve done a lot of reflection, and in my experience using a @property instead of an explicit release/= nil solves more problems then it causes. So I think it’s the best practice.

Even if you disagree with me on that point, if the only places you explicitly NARC objects are init, dealloc, and setX: methods then I think you’re doing the right thing.

Cycles!

The last piece of the memory-management puzzle are retain cycles. By far the best advice I’ve seen on them is Mike Ash’s article. Read it.

Older Posts »

Powered by WordPress