Uli's Web Site
[ Zathras.de - Uli's Web Site ]
Other Sites: Stories
Pix
Abi 2000
Stargate: Resurgence
Lost? Site Map!
 
 
     home | blog | moose | programming | articles >> blog

 Blog Topics
 
 Archive
 

15 Most Recent [RSS]

 Less work through Xcode and shell scripts
2011-12-16 @600
 
 iTunesCantComplain released
2011-10-28 @954
 
 Dennis Ritchie deceased
2011-10-13 @359
 
 Thank you, Steve.
2011-10-06 @374
 
 Cocoa Text System everywhere...
2011-03-27 @788
 
 Blog migration
2011-01-29 @520
 
 All you need to know about the Mac keyboard
2010-08-09 @488
 
 Review: Sherlock
2010-07-31 @978
 
 Playing with Objective C on Debian
2010-05-08 @456
 
 Fruit vs. Obst
2010-05-08 @439
 
 Mixed-language ambiguity
2010-04-15 @994
 
 Uli's 12:07 AM Law
2010-04-12 @881
 
 Uli's 1:24 AM Law
2010-04-12 @874
 
 Uli's 6:28 AM Law
2010-04-12 @869
 
 Uli's 3:57 PM Law
2010-04-12 @867
 

More...

UKHelperMacros

One of the bits of code I use a lot is a collection of a few small macros, which I can #import from my precompiled header. It contains a few neat ones:

UKLog

Even with Xcode 3's mini debugger that lets you debug an application without bringing it to the front, and with the ability to use dtrace (aka Instruments and X-Ray) to set breakpoints that will fire the third time in only, or display certain variables, you'll often find yourself needing to do what people used to call printf-debugging. I.e. you'll be scattering NSLog statements throughout your code, which print some useful bit of information.

Now, users generally don't appreciate applications that have a case of Logorrhoea and keep spilling messages onto their hard disk. So, you have to remember to remove all those NSLogs later. Or, you write a macro. UKLog is one like this. What I do first is to go to the Project Settings window in Xcode, and there to the build settings for the "Development" build configuration. There, I add DEBUG=1 to the "Preprocessor Macros" entry.

Now, I can use #if to detect whether my code is being built for development or deployment, and map UKLog to NSLog for development builds, and to nothing for release builds:

#if DEBUG
#define UKLog(args...)    NSLog(args)
#else
#define UKLog(args...)    // do nothing.

This is a variadic macro, that is, I can pass an arbitrary number of parameters to it, and it'll always pass those parameters to NSLog.

But while we're doing this, we may as well make the message better. It's always a bit annoying to have to locate the method that printed a particular bit of text, so why not include that:

#define	UKLog(args...)    NSLog( @"%s: %@", __PRETTY_FUNCTION__, [NSString stringWithFormat: args])
__PRETTY_FUNCTION__ is a preprocessor constant defined by GCC that contains a C string with the name of the current function. So, this will log something like:
-[MyObject release]: User didn't call -defrobnitz before releasing.

ASSIGN and DESTROY

One of the more annoying aspects of Cocoa programming is writing accessors and destructors. Especially if you're paranoid enough to insist on clearing released instance variables to NIL. So, I wrote myself two macros to help me with this:

#define DESTROY(obj)		do {\
					[obj release];\
					obj = nil;\
				} while(0)

Ignore the do { } while(0) part. That's just so this macro's several statements don't confuse if, then and other control structures, and behaves just like a function call. This way, your destructors turn from:

-(void) dealloc
{
    [foo release];
    foo = nil;
    [bar release];
    bar = nil;
    [super dealloc];
}
into:
-(void) dealloc
{
    DESTROY(foo);
    DESTROY(bar);
    [super dealloc];
}

Moreover, if you're writing a library that should be usable both using taditional reference counting and using the garbage collector, you can change this to simply set variables to NIL when built for GC, and thus the same code will behave correctly for both cases. Of course, that's only useful if you use DESTROY elsewehere than your destructor, because destructors don't get called under GC.

The second macro is ASSIGN:

#define ASSIGN(targ,newval)	do {\
					id __UKHELPERMACRO_OLDTARG = (id)(targ);\
					(targ) = [(newval) retain];\
					[__UKHELPERMACRO_OLDTARG release];\
				} while(0)

This simply takes care of releasing the previous object pointed to by an instance variable and retaining the new one. Your mutators will do the following transformation:

-(void)    setFoo: (NSString*)inFoo
{
    NSString*   old = foo;
    foo = [inFoo retain];
    [old release];
}

-(void)    setFoo: (NSString*)inFoo
{
    ASSIGN(foo,inFoo);
}

Less typing, and more importantly, less opportunity for mistyping. However, note that this macro expects the first argument, targ, to be an instance variable. If it is an expression, this expression will be evaluated twice, so don't pass *(++myArray) to it, or you'll increment two times, and have a stale pointer in the first and smash the pointer in the second.

CREATE_AUTORELEASE_POOL

When you start a new thread, or have code in your main() function, you generally have to create an autorelease pool so you don't leak objects:

NSAutoreleasePool* aPool = [[NSAutoreleasePool alloc] init];

// Code goes here...

[aPool release];
aPool = nil;
With our macros, you have to type a bunch less (for one, you only have to type "NSAutoReleasePool" once), and again it's less likely you'll make a typo:
CREATE_AUTORELEASE_POOL(aPool);

// Code goes here...

DESTROY(aPool);

These macros don't just let you avoid typos, at the same time they make your code more readable, and the pool creation/teardown and object creation/teardown calls will stand out more. And it'll enforce good coding practices, as all released object pointers get zeroed and you can't get weird behaviour from using stale pointers anymore.

And finally, you could do your own zombie-detection by using some object instead of NIL in debug builds, kind of like NSZombie works. However, these objects, *you* control, so you can easily make them behave like a NIL object, except when a particular method is called where you know someone is messaging a released object.

You can get these UKHelperMacros on my Source Code page. The UKHelperMacros are free to use, change and redistribute, as long as you don't distribute modified versions and claim I had done the modifications, so feel free to use them wherever you feel like it.

By the way: GNUstep defines its own ASSIGN, DESTROY and CREATE_AUTORELEASE_POOL macros, so I decided to use the same names and parameters to encourage creation of a standard for this.

Reader Comments: (RSS Feed)
Harvey Swik writes:
ASSIGN/DESTROY I can see using something like this for Tiger, but for Leopard why not use properties? DESTROY(someAutoreleasePool) In the case of NSAutoreleasePool, you probably want [somePool drain] instead of [somePool release] for forward compatibility with GC. Works on Tiger too. UKLog I use something similar to this: #define UKLog(format, ...) NSLog( @"%s: %@", __PRETTY_FUNCTION__, [NSString stringWithFormat: format, ## __VA_ARGS__]) It's been a while since I touched that so I'm not sure if there's any practical difference between the macros.
Uli Kusterer replies:
@Harvey: Yes, if you can be Leopard-only, then you can use properties. But most people will probably want to be compatible with 10.4, and this is a way to alleviate the pain of knowing one could be using properties. Regarding destroying a pool: You don't need autorelease pools in a GC world. Why would you call drain? I'd just define out the create autorelease pool call. Regarding your log method: That's effectively the same as mine. Just that mine includes the format in the va_list, and yours has it separately, and hence has to remove the comma using ## if there are no variadic arguments. Though your version is ANSI standard, I think, while my args... is a GCC extension.
Or E-Mail Uli privately.

 
Created: 2007-11-01 @054 Last change: 2024-11-15 @661 | Home | Admin | Edit
© Copyright 2003-2024 by M. Uli Kusterer, all rights reserved.