August 21, 2005

Pimp My Code, Part 4: Returning late to return early

First off, I apologize for how long it's been since my last post. It's been a bit nutty around Delicious Monster these last couple weeks, and I had to get a 1.5.2 release out as well. Also, my main machine lost its hard drive, which took two weeks to restore and repair. Also, I got the flu really bad. Also, I started dating again, which is one of those things you'd think I'd learn to stop doing, since by definition every relationship, no matter how short or how long, ends in pain.

Anyways, if you've read this series from the beginning, you'll know that, besides being perenially single, I also insist on returning early from functions/methods/what-have-you, instead of nesting ifs inside ifs inside ifs. To me, this makes the code much quicker to understand at a glance -- the normal path is the one down the left column, and any if statements I see are just dealing with error conditions. I demonstrate this in an earlier column, so I won't paste it again here.

Though I will say, again, that the goal of good coding is to make your code apprehensible at a glance. That's the goal, the as in only. I've spent a 25 years learning this rule, and I'll keep harping on it until St. Peter hands me a different harp to play. (Then, I plan to play "Stairway to Heaven".)

Now, several people have asked a thorny question, including one perceptive man named Tobias, who I will quote, in part, here:
I do have a problem with your solution though, and I can't figure out a way to solve it, so I thought I might ask you and see if you have an answer. What if you want some specific code that's executed when bailing? Or say, some code that should always be executed after some statements to clean up? I can't use return; then to bail, because I need that code to be executed before returning.

Ah, young Tobias. Condescending comment. Witticism!

Actually, to be honest, I don't have a really good solution to this one. I have solutions I use, depending on the situation, but none of them are particularly pleasing. I still like them better than nested-ifs, and I've usually found that when unrolling nested-ifs, there were lots of ways the code could have failed and not been handled correctly; that is, nesting ifs is actually not a particularly robust way to handle errors.

So, here are some possible situations, and possible solutions to them:

Situation 1 is if you've got a couple of lines of code you'de like to execute before you return from several different places, but you don't need to put the return statement itself in those lines; eg, the return is simple enough that it's not a hardship to copy and paste it. For this we use an inline function. It's a good fit because it's scoped right (won't be visible from anywhere but inside this method), and it inherits the scope above it, so you can use "self" and access your instance variables.

This particular snippet is from the code I use to recognize whether a keypress that's coming in to the program is part of a scanner's input or if it's being pressed by a human, since some scanners ("wedge-type") just send USB keypresses as if they were just another keyboard, and it's freaking hard to figure out where a keypress came from in AppKit. Oh, sure, they claim there's a way, but I will literally give $200 to the person who can show me how to take a keypress NSEvent and get the device that it came from out of it. No B.S., cash money. (Or an interview!)

Situation 1: Couple lines of cleanup code before returning

inline void _sendAllEventsIncludingThisOne() {
[self _sendNormallyStoredNumericKeypressEvents:nil];
[super sendEvent:theEvent];
}

unsigned int modifierFlags = [theEvent modifierFlags];
// scanners never press the shift or command keys...
if ((modifierFlags & 0xffff0000) != 0) {
_sendAllEventsIncludingThisOne();
return;
}

unsigned int length = [[theEvent characters] length];
if (length != 1) { // WJS: pretty much all keydown and keyup events
have exactly one character these days
_sendAllEventsIncludingThisOne();
return;
}


Well, it's not pretty, but it does the job. One nice thing to note about this kind of approach is you can have different little "deconstructor" inline functions for dissassembling different pieces of a whole that you are assembling, so you could stack them intelligently if you get further along and fail, and thus require more unrolling actions. If you need to return some complex value when you bail, have your inline method return it, and then just call something like "return _sendAllEventsIncludingThisOne();"

In Situation 2 we see a situation where we have one exit point, and we pretty much always want to do the same thing in front of it.

Situation 2: One exit point at end with cleanup code

{
autoreleasePool = [[NSAutoreleasePool alloc] init];

if (![self _drinkIsFrosty]) // this can allocate a lot of memory
goto bail;

if ([self _drinkType] != wantedDrinkType)
goto bail;

// ingest drink here

bail:
[autoreleasePool release];
}


Remember that this technique isn't a panacea: if you set up some other state in this method that needs to be unrolled, you're going to have to deal with it.

Here are some other real-life macros I've written. These could have easily been inline function, but I wrote them as macros because when I wrote them I wasn't sure if I was going to insert return statements into them; if you return from an inline function you just pop back to the method that had the error, but if you return from a macro you are actually returning from the containing method. This is sometimes a good thing, and sometimes a bad thing, and you have to decide which you want.

Note that these are the macros I use when dealing with QuickTime, so I can be notified if an error occurs and then decide if it's critical enough that I want to throw an exception. The problem with QuickTime's error model is there are a lot of errors that aren't critical and won't affect whether the program will continue correctly, so you have to sort your way through the values by trial and error at run time to figure out what errors you'll see in practice and what they mean. Oh, man, I sure DO NOT MISS this kind of programming. Bring on the Cocoa QuickTime!

Situation 3: How I deal with Carbon APIs

#define NoteErr(x) { if ((x) != noErr) NSLog(@"QuickTime note error %d in file %s
on line %d", (x), __FILE__, __LINE__-1);}
#define PedanticErrorCheck() { OSErr _err = GetMoviesError(); if (_err != noErr) NSLog(@"QuickTime warning error %d in file %s
on line %d", _err, __FILE__, __LINE__-1);}
#define BailErr(x) { if (x != noErr) [NSException raise:[NSString stringWithFormat:@"%d",
x] format:@"Raised Carbon error %d in file %s on
line %d", x, __FILE__, __LINE__-1];}

...

{
...

sequenceGrabberComponent = OpenDefaultComponent(
SeqGrabComponentType, 0);
if (sequenceGrabberComponent == nil)
BailErr(-1);

err = SGInitialize(sequenceGrabberComponent);
BailErr(err);

// specify the destination data reference for a record
operation -- tell it we're not making a movie
// if the flag seqGrabDontMakeMovie is used, the sequence
grabber still calls your data function, but does not write
any data to the movie file
// writeType will always be set to seqGrabWriteAppend
err = SGSetDataRef(sequenceGrabberComponent, 0, 0,
seqGrabDontMakeMovie);
BailErr(err);

// create a new sequence grabber video channel
err = SGNewChannel(sequenceGrabberComponent,
VideoMediaType, &sequenceGrabberChannel);
BailErr(err);

...
}


As I said, this is work-in-progress. None of these techniques are super-beautiful, but when I compare my sequence-grabbing code to the samples on the websites, I certainly think this is a HUGE improvement. Especially since usually the examples just say, "/* if there's an error, you should handle it. */" Yah, thanks, guys. We CERTAINLY would NOT want an example of how to tear down these structures that are in a primordial state.

This brings me to a side rant. Seriously, if you're going to post sample code, SHOW HOW TO HANDLE ALL CONDITIONS! In the real world, windows resize. Users hit the wrong buttons. Cameras get unplugged. We need to know how to handle these things! There are still things that the iChat team can do with the iSight camera that I cannot replicate, and I think that stinks.

In summary: here's a situation where I'm actually INVITING you to flame me and post a better way of doing things. Considering the response on my last couple "pimp" articles, where I specifically told people not to, I'm expecting an avalanche of fun on this one.

Labels:

40 Comments:

Anonymous Anonymous said...

For 10.3 and later, @finally is an option.

- (void)doDomething
{
   NSAutoreleasePool   *pool = [[NSAutoreleasePool alloc] init];

   @try
   {
       if (! something)
           return;
           
       // do something else
   }
   @finally
   {
       [pool release];
   }
}

August 21, 2005 6:15 AM

 
Anonymous Anonymous said...

I believe that by using and (&&) you can refactor every nested if then else . so there is no need for bailing out at all.

August 21, 2005 6:52 AM

 
Anonymous Anonymous said...

I believe that by using and (&&) you can refactor every nested if then else . into a
if then elseif elseif else
so there is no need for bailing out at all.

August 21, 2005 6:57 AM

 
Blogger Marcus S. Zarra said...

First -- Welcome Back Wil!!! You were missed.

As someone mentioned above me, it seems like a try/finally would be a better solution. Coming from a Java background, that is the way I am used to solving this situation and it certainly seems cleaner than a macro or inline function.

August 21, 2005 8:47 AM

 
Anonymous Brian Webster said...

One pattern I use to avoid nesting too many ifs is a pattern looking like this:

- (NSError*)myMethod
{
NSError* error = nil;

error = [self doFirstStep];
if (error == nil)
{
error = [self doSecondStep];
}
if (error == nil)
{
error = [self doThirdStep];
}
....etc...
//cleanup stuff
return (error);
}

This maintains a linearity to the code, since any error by one step will simply cause it to skip over the rest of the steps of the method. Any cleanup code that needs to be executed can go right at the bottom.

Again, like any other form of error handling, this won't handle all cases, but I find a lot of code can be written this way and it's fairly easy to follow the flow.

It can get a little trickier if you have to handle error conditions of different types (NSError, OSStatus, errno, exceptions, and so on). I usually try to funnel everything into one error type, NSError has worked well for this, since it can wrap itself around any other kind of error that comes up.

August 21, 2005 10:19 AM

 
Anonymous Anonymous said...

Fool! This stuff is easily avoided by wrapper code. Don't they teach you kids anything these days ?

This miserable attempt
{
   autoreleasePool = [[NSAutoreleasePool alloc] init];

   if (![self _drinkIsFrosty]) // this can allocate a lot of memory
      goto bail;

   if ([self _drinkType] != wantedDrinkType)
      goto bail;

   // ingest drink here

bail:
   [autoreleasePool release];
}


would be rewritteny by any self respecting programmer thusly

- (void) _ingestDrink
{
   if (![self _drinkIsFrosty]) // this can allocate a lot of memory
      return;

   if ([self _drinkType] != wantedDrinkType)
      return;

   // ingest drink here
}

- (void) ingestDrink
{
   NSAutoreleasePool *pool;

   pool = [NSAutoreleasePool new];

   [self _ingestDrink];

   [pool release];
}


And using @finally instead of NS_DURING.. give me a break!! This has worked for years and always will work beautifully

   pool = [NSAutoreleasePool new];
   exception = nil;
NS_DURING
   // stuff
NS_HANDLER
   exception = localException;
NS_ENDHANDLER
   [pool release];
   [exception raise];


Tip: Use the desireable alloc/init pattern instead of new to bloat your binaries.

August 21, 2005 11:21 AM

 
Anonymous Anonymous said...

Fool, the wrapper approach doesn't work when _ingestDrink has locals that may need to also be released.

I see nothing wrong with @finally - it is a real language feature rather than a macro hack, whether or not the macro hack has worked well for years.

@finally also provide a single block where code is guaranteed to be executed regardless of how the function was exited.

You can do the same with NS_DURING, but it is a bit more work.

August 21, 2005 12:04 PM

 
Blogger Wil Shipley said...

@finally:

Wow, this does look awfully cool. I'm 10.3-only, too! There's not a ton of documentation on it, but I did finally find some at Apple's dev site.

Brian:

I still find that kind of approach very hard to read, because it puts the emphasis on the error-checking, not on the actual code. EG, most of the lines you see are for error-checking, and the actual content is all indented.

Fool!:

The previous poster pointed out that the problem with NS_DURING that @finally avoids is that you can't call a nice naked "return" inside an NS_DURING block. You have to add extra crap. I'm not even clear on if NS_ENDHANDLER is called if you use one of the NS_RETURNs, although it might be. It's certainly not if you return out of the NS_HANDLER portion, so it's not really syntactically the same thing.

I'm not really keen on creating lots of helper methods. I know when people first learn object-oriented programming they like to go crazy and create a zillion methods, but I've found that every method you add to a class has a HUGE negative impact on readability. Also, I think it's stylistically ugly to put private methods right in-line with public ones. And, finally, I think it's just harder to read code that goes "start here -- jump up to somewhere else -- jump back -- end here". Code should flow from beginning to end in a straight line, like a stream.

-W

August 21, 2005 3:37 PM

 
Blogger Wil Shipley said...

Alloc/init vs new:

Ok, this is just silly. In all of Delicious Library, for example, I call +alloc 693 times. Let's assume it takes, say, 16 bytes to do a method call. Well, wow, you're right, I burned almost a whole 8K out of my 4 megabyte exectutable on my antiquated ideas.

What are you, doing embedded Objective-C programming?

If you're going to worry about such trivialities, I'll be forced to point out that +new is _slower_, since it has to make method calls to +alloc and then -init, just like you would, but you also had to make a method call to +new itself. Now, if you're inside a tight loop allocating and deallocating objects, is the extra 16 bytes in your cache going to hurt you more, or is the extra method call? (Hint: it's the latter.)

I also think +new is harder to read. It introduces a new method to the reader that's not necessary.

"Hey, what's +new?"
"Oh, well, it's just +alloc and then -init."
"Well, what if I want to call -initWithFrame:"
"Then you do it the old way."
"So why are there two ways?"

August 21, 2005 3:50 PM

 
Anonymous Chad said...

Hmmm...ultimately, a lot of this sounds like a difference of opinion and style. As I was taught, one entrance - one exit. So, in my method of learning, there probably would be nested IF statements, whether for good or evil. But then again, global variables are supposed to be inherently evil, but perhaps that is given the stigma to make the young programmer weary of some concepts. When used sparingly and properly, then perhaps some concepts can be OK, but one does need to be careful.

August 21, 2005 5:28 PM

 
Anonymous Chris Hanson said...

@try, @catch, and @finally are interoperable with NS_DURING/NS_HANDLER/NS_ENDHANDLER but offer additional functionality.

For example, you can do type-based dispatch with multiple @catch blocks just like in Java. (Cocoa tends to just use NSException everywhere for historical reasons, but that doesn't mean your code has to.)

Also, since they're part of the language itself the new exception handling constructs will cause the compiler to automatically promote your variables to volatile when necessary. This is not the case for the macro-based exception mechanism, you have to understand when and how to do this by hand.

A @finally block will be invoked regardless of how its associated @try block is exited, whether from an internal return or an exception. (The only exceptions (ahem) I can think of would be a non-raise/non-@throw longjmp or a C++ exception.)

The new mechanism also integrates cleanly with @synchronized. For example, a @throw out of a @synchornized block will unlock the resource being @synchronized upon. Whereas with a regular NSLock, you'd have to handle this using a @finally block. (There are still plenty of reasons to use NSLock or a subclass though, particularly if you need to handle conditions or recursive calls.)

August 21, 2005 6:37 PM

 
Anonymous Anonymous said...

i don't know anything about code but it's really cool to see all you cats getting down in your native language!

love,
a music nerd

August 21, 2005 9:36 PM

 
Anonymous Topher said...

I have to say, the @try/@finally design is easily readable, and the wrapper with a private method is not. And I have no idea what's going on with the NS_WHATEVER stuff. If the goal is for the code to make sense, @try/@finally seems to be the way to go, hands down.

August 21, 2005 10:39 PM

 
Anonymous flies said...

Hrmm.... I suspect one would have to create an input server. Was it a guess... maybe... maybe not.... maybe.

August 21, 2005 10:57 PM

 
Anonymous Anonymous said...

Fool, the wrapper approach doesn't work when _ingestDrink has locals that may need to also be released.

I am not surprised that I have to explain everything here. There weren't any local variables in the original example, now where there ? If there were they would have been passed in as parameters.

Onto more of the straw grasping...

I also think +new is harder to read. It introduces a new method to the reader that's not necessary.

Yeah right. And then it's also harder to type...

It would be rather a unfamiliar method to the writer, because the reader is probably well versed in other OO languages, which incidentally dont' use alloc/init in case anyone wonders.

As +new is much easier to type than alloc] init and since the resulting output is smaller: If you have some pride in your craft, the choice should be clear.

I'll be forced to point out that +new is _slower_, since it has to make method calls to +alloc and then -init

Pitiful.

+alloc is calling +allocWithZone: and +new is calling +allocWithZone: (not alloc) so in fact the number of method calls is identical.

August 22, 2005 8:51 AM

 
Blogger Marcus S. Zarra said...


+alloc is calling +allocWithZone: and +new is calling +allocWithZone: (not alloc) so in fact the number of method calls is identical.


Where are you getting this from? According to Apple's documentation +new calls alloc and init. According to GNUStep's source code it is calling alloc and init. In neither case is allocWithZone mentioned.

Perhaps you have access to the source code for Foundation?

As for the wrapper code, given a choice I would much rather see a @try/@finally block then a mess of small private methods like you proposed.

August 22, 2005 12:13 PM

 
Anonymous Anonymous said...

"'Hey, what's +new?'
'Oh, well, it's just +alloc and then -init.'
'Well, what if I want to call -initWithFrame:'
'Then you do it the old way.'
'So why are there two ways?'"


I thought that +new was the old way. The way I was told when I first came across it was that [SomeClass new] was the "NXWay" (that is, the old school NeXTies did it that way) and that [[SomeClass alloc] init] was the "NSWay" that was adopted by Apple for Rhapsody and Mac OS X.

Is that not the way it went down?

August 22, 2005 12:40 PM

 
Anonymous Anonymous said...

So I'm a single point of return kind of guy. I think it's easier to read (you shouldn't have massive methods anyway), easier to debug and less error prone. If you do it right you're dropping through the error checks on success not on failure which makes the body of the function doing stuff rather than dealing with errors. It also allows you to consolidate your error handling rather than copy and pasting crap all over the place.
Gideon S.

August 22, 2005 1:05 PM

 
Anonymous Anonymous said...

Where are you getting this from? According to Apple's documentation +new calls alloc and init. According to GNUStep's source code it is calling alloc and init. In neither case is allocWithZone mentioned.
What drugs are you on?

You obviously went and looked at the gnustep code for + (id)new, but you didn't take the time to look at + (id)alloc. Here you go:

/**
* Allocates a new instance of the receiver from the default
* zone, by invoking +allocWithZone: with
* NSDefaultMallocZone() as the zone argument.
* Returns the created instance.
*/
+ (id) alloc
{
return [self allocWithZone: NSDefaultMallocZone()];
}

Now for further clarification
From NSObject's documentation from Apple


+ (id)alloc

Returns a new instance of the receiving class. The isa instance variable of the new instance is initialized to a data structure that describes the class; memory for all other instance variables is set to 0. The new instance will be allocated from the default zone—use allocWithZone: to specify a particular zone.
An init... method should be used to complete the initialization process. For example:


This would imply that it calls allocWithZone with the default zone.

August 22, 2005 4:00 PM

 
Blogger Wil Shipley said...

+new is, in fact, the _old_ way, but since it's pretty much deprecated a lot of people discover it after learning +alloc -init, and so the hypothetical conversation is from their point of view, asking why they'd use +new instead.

I'm of the opinion that having additional methods in the API that do the same thing is just confusing to everyone involved. Our anonymous and belligerent friend obviously disagrees, but I'm OK differing with him on this point.

In fact, I'm beginning to suspect he's looking for pretty much anything to complain about, in order to "take me down a peg." Which is fine, I'm sure I present a large target.

August 22, 2005 4:19 PM

 
Blogger Marcus S. Zarra said...

It definitely appears that Mr. Anonymous is just looking to pick a fight. Especially since he likes to switch back and forth on his arguments based on which ever proves his "point".

But in response -- yes alloc calls allocWithZone with the default zone. No one said it didn't. The original point which you are now ignoring is that +new introduces an additional message which was Wil's point originally.

+new calls
alloc which calls allocWithZone
init

4 messages

alloc calls allocWithZone
init

3 messages.

You do the math instead of quoting irrelevant documentation and flip flopping on your point.

August 22, 2005 7:04 PM

 
Anonymous macFanDave said...

Also, I started dating again, which is one of those things you'd think I'd learn to stop doing, since by definition every relationship, no matter how short or how long, ends in pain.

That's not true! Some end in death. In fact, that's what you promise when you make your final exit from the singles world.

On balance, I prefer the problems of married life to those of my former single life. And when you factor in the daughter, it's a blow-out victory for marriage!

The key, Wil, is to lower your standards. That's what my wife did and she ended up with me ;-)

August 23, 2005 6:16 AM

 
Anonymous Anonymous said...

That's not true! Some end in death. In fact, that's what you promise when you make your final exit from the singles world.

I think that was Wil's point when he said no matter how short or long, ends in pain. You surely aren't going to be doing cartwheels down the hall when your wife passes on.

August 23, 2005 6:26 AM

 
Anonymous Anonymous said...

As I wrote: +new calls +allocWithZone: directly. So that makes three calls. Rationalizing or wishing it to be otherwise doesn't make it so. That is on Mac OS X. (10.4 to be pedantic).

As I´ve come to know the clientele here, I probably need to spell it out, how to verify this. Get a programmer to subclass NSObject for you and to implement +allocWithZone: and +alloc: . Then call +new and witness how +alloc is not used.

As people seem to be comfortable and secure in their rote, I will not waste my energy further on this thread to educate. I found it quite telling, though expected, that people latched onto the perceived "performance thing" about alloc/init, ignoring the pearls I casted...

Finally lets not get teary-eyed so quickly - when asking to be flamed and someone turns up the heat a little.

August 23, 2005 6:36 AM

 
Blogger Marcus S. Zarra said...

A flame from an anonymous coward is no flame at all. --me

After all of that you finally answered the single question I had for you Mr. AC. Although personally I will continue to use alloc and init over +new since it is more consistent. It is good to know that the boys over at Apple made it as efficient as calling alloc-init even though OpenStep calls alloc instead of allocWithZone.

August 23, 2005 8:00 AM

 
Anonymous Mason Mark said...

Also, I started dating again, which is one of those things you'd think I'd learn to stop doing, since by definition every relationship, no matter how short or how long, ends in pain.


That's not true! Some end in death. In fact, that's what you promise when you make your final exit from the singles world.


Well, life itself is definitely a single-point-of-return kinda thing.

Most implementations of dating, on the other hand, are rife with unhandled exceptions. You never can quite predict where and how it will return. In some case, dating can even be re-entrant, but that starts to get really messy!

August 23, 2005 8:34 AM

 
Anonymous Bryce M. said...

NOOOOOOOOOOOOO!!!!!!!

If Wil gets a girl there'll be no more delicious software written - she'll suck up all of his time and energy!!!! That's what girls do!!! (apart from shopping that is.)

:-P

Seriously, best wishes and I hope things work out for you.

ps thanks for the code tips too.

August 23, 2005 9:24 PM

 
Blogger Wil Shipley said...

Oh, man, I'm really going to miss having that "idiot" guy around. It's too bad we weren't more appreciative of his abusive advice. It's strange, isn't it, how stupid people like us never really properly show our gratitude when smart people deign to stoop down and throw a few pearls our way. Then, we they leave, we have nothing with which to console ourselves but the sudden lack of insults.

For the record, I didn't actually ask to be flamed. I asked people what their approach to this problem would be, and I got some wonderful responses, including the "@try" thing, which I will, in fact, try.

I didn't say, "Hey, please call me and all my readers idiots and then state something as unequivocally true without a ton of supporting evidence."

Honestly, if you want to express your opinions without much support AND have people just trust that your way is better, then state your pedigree. For instance, I give my opinions on matters of taste all the time, but any reader is welcome to decide that she thinks OmniImage and OmniPDF and OmniWeb and OmniGraffle 3 and Delicious Library were crap, and do things her own way.

-W

August 24, 2005 12:37 AM

 
Blogger Andreas said...

Coming from a C++/Java background, I find the @finally method to be the most elegant one.
One other option, besides gotos, would be to (ab)use switch() and break:

autoreleasePool = [[NSAutoreleasePool alloc] init];
switch(true){ // "try"

if (![self _drinkIsFrosty]) // this can allocate a lot of memory
break;

if ([self _drinkType] != wantedDrinkType)
break;

// ingest drink here

}
// "finally"
[autoreleasePool release];

August 24, 2005 3:46 AM

 
Blogger Andreas said...

@Wil: you wrote

In summary: here's a situation where I'm actually INVITING you to flame me and post a better way of doing things.

Be careful what you wish for ;)

August 24, 2005 4:10 AM

 
Anonymous gp said...

Marcus S. Zarra said:
A flame from an anonymous coward is no flame at all. --me


Hello? This is the Internet you know. Calling someone an anonymous coward just because you have a name and a photo at your blogger profile is obviously trolling.

August 24, 2005 10:44 AM

 
Blogger Marcus S. Zarra said...

Hello? This is the Internet you know. Calling someone an anonymous coward just because you have a name and a photo at your blogger profile is obviously trolling.

Try reviewing the comments that I was replying to before you flame me for bothering to put my name behind my comments.

This is indeed the internet and flaming anonymously still counts for nothing.

August 24, 2005 2:56 PM

 
Anonymous gp said...

You don't get it. Anyone can use a random name and a random picture and create a profile. That doesn't make you eponymous.

Since I can't verify that Marcus S. Zarra is your real name, you are also an anonymous coward as far as I am concerned (and so am I of course).

August 24, 2005 4:14 PM

 
Anonymous corwin said...

This thread has turned unexpectedly hilarious, into some kind of badly-staged internet brawl.

Code is a craft. It's inexact. Writing code that follows everyone else's style is like writing poetry in the same meter and pattern as everyone else. Think a little, realize that every little quirk your code imparts is a little part of you, and get over making it "perfect". Read others' code like you would poetry- take inspiration from it, learn from it, sometimes imitate it, but never take it as a personal affront.

At the end of the day, you should ask yourself: Did the code I write make art? Will it make a user's life easier? Did it make me feel good?

If you can answer those questions yes, you're OK. All else is void.

August 25, 2005 12:08 AM

 
Blogger Marcus S. Zarra said...

gp,

Everyone has an opinion. Thank you for expressing yours.

If you don't understand the simple concept of putting a name, any name behind your words and therefore building a reputation behind that name then there is simply nothing else to discuss. How do we even know this is Wil Shipley's blog? I remember reading, with great entertainment, a blog purportedly written by Steve Jobs. However, a simply google search turned up the truth.

If you cared, you could quite easily verify my identity and doing a trivial google search will turn up plenty of information on me. The point, which was missed above, is that I stand behind my words. If you are going to disagree with someone, stand up and be recognized. Then your words will be valued based on what they say and where they come from. Unlike our temperamental AC above who claimed neither substance or experience.

The heckler in the back of the room is always ignored.

Wil,

I apologize for the OT discussion. I tend to get long winded at times. I look forward to your next chapter. So far your discussions have been invaluable to me and my growing understanding of Cocoa and Objective-C.

Corwin,

It definitely seems like Wil's opinions on coding turn up the ugly doesn't it. I would love if some of these nay-sayers would put up their code to internet review. I doubt they would fair as well.

August 25, 2005 9:28 AM

 
Blogger Stoneface said...

Wil, thank you for posting this blog. Not just this entry, but the whole blog so far.

Your opinions and insights are usually right up my alley.

September 10, 2005 3:05 AM

 
Anonymous Anonymous said...

Late again, but: NS_ENDHANDLER does not compare to @finally. In fact, it’s functionally equivalent to the } at the end of a @catch block. It can’t be “called,” and its presence is (and must be) ignored when an exception is thrown.

A problem with the @try thing for general cleanup is that the exception mechanism is (even on x86en) based on a high-overhead setjmp()/longjmp() pair, so each @try requires substantial setup. Apple had the perfect opportunity, with the x86 switch, to switch to a no-overhead solution such as used for C++ and define NS_DURING etc in term of @try, but they missed it. :-(

July 27, 2006 2:20 AM

 
Anonymous Martin said...

Wil,

Does the inline function from your first example work as it is? I tried to write one like it and the compiler complains about `self' being an unknown identifier, which is true, as it is a function and not a method, and it still needs to be compiled by itself.
I hoped that for a static inline function that's only called from methods, the compiler could prove that it's only ever used in a context where self is defined, but that's not the case, and it would have broken function semantics.
I would use a macro instead, but I find those more painful to read (and write), and I really wanted a block of statements that returns a value (i.e. a function).

So, um, does that code actually work? Is there a way to use `self' in an inline function?

August 26, 2007 1:14 AM

 
Blogger Wil Shipley said...

Martin:

I should have been more clear -- that inline function was embedded inside a method, using a cool feature of c99.

Sadly, due to the security reasons detailed by ridiculous fish (see my blogroll for his blog) you can't define functions inside functions (or methods) any more, so you can't use this trick. Just pass 'self' as the first parameter to your inline functions and you'll get much the same effect, though.

-Wil

August 26, 2007 11:19 AM

 
Anonymous Alex Gordon said...

To settle this completely (and to satisfy my curiosity), new has no overhead over alloc/init.

+new Calls:
new
allocWithZone:
init
+alloc/-init Calls:
alloc
allocWithZone:
init

I wrote an app to test it:
Download!

So it seems if you want the best performance you should be using something like this

#define NEW(class) [[class allocWithZone:NSDefaultMallocZone()] init]


But all of this is really unnecessary. Certainly the readability of the code is far more important than one extra method call. If one extra method call is such a problem, well you shouldn't be using Objective-C.

August 31, 2007 4:52 AM

 

Post a Comment

<< Home