The crux of the issue
" The effect is that if you send a message to a nil object using a selector that's expected to return a boolean, int, float, etc, you'll effectively get NO, 0, 0.0 (respectively) back *because* the appropriate value is already in r3. The effect is the same with the Intel implementation. 'Self' is passed in the EAX register, and EAX is used for the return value. This is -- I believe -- the reason why GCC ensures that Self is the last value that's placed in EAX immediately before the dispatcher call."
Aha. I didn't think about that. Honestly, I try to avoid depending on this activity, to a degree. No, I don't check before [fooObject release]; but I'd avoid {if ([fooString length]) }, using {if ((fooString != nil) && ([fooString length] != 0)) }. The latter, while chattier, is actually faster, safer, etc. True, it uses a couple more bytes. But the checks against 0, if I'm not mistaken simplify out to the same assembly. And a nil will reduce another function call of send_msg. More importantly, here's where you're fighting resistance.
This bit about handling nil sounded familiar. Indeed, it's a really nasty design tradeoff. You don't always get 0 back from a nil call.
http://ridiculousfish.com/blog/archives/2005/05/29/nil/
r3 is 0, true, but r4 (The lower half of a long long return value) is the selector, and fpr1 is the first floating argument in, and the floating result out. And none of the registers have been changed. In other words, for PPCs
- (UInt32) [nil fooInt]; returns 0.
- (UInt64) [nil fooLongLong]; returns (long long)(@selector(fooLongLong)) on 32 bit systems, but 0 on 64-bit systems.
- (float) [nil fooFloat: (float) barArg]; returns barArg.
- (float) [nil fooFloat]; is undefined.
There needs to be a happy medium between tight code and flexible code. And there is a lot of bloated code out there, regardless of language. But if you over-optimize, bad things happen. This isn't to diminish your message. Honestly, I enjoy your articles, despite playing the devil's advocate so often.