Saturday, October 22

Drunk and Blog

Chicago is certainly my kind of town. The little I saw of it, that is. The little I saw of it that wasn't traffic.

There's a lot of traffic in Chicago. It's worse in Seattle, but I know my way around Seattle so I avoid it. Chicago, we get in a cab, we creep along and honk at people. Creep, honk, creep, honk.

They like honking at people in Chicago. Street signs and stoplights are really considered guidelines, there. Running reds is de rigueur, because by the time the people blocking the intersection during your green clear out it's red again, and so you've got to run 'em to get anywhere. It's like the magazine wars where every magazine wants to be first with the December issue, so now December comes in October.

We actually had a guy honk (a LOT) and roll down his window and throw f-words at us for crossing in a controlled intersection when we had a white "Walk" signal. He wasn't alone; there were three cars behind him as well, all offended that a huge group of pedestrians would dare to be crossing a street with the light in the world-famous(?) Magnificent Mile shopping district. I was so angry I stood in the street and pointed at the "Walk" sign and yelled back. I don't think I was getting into the proper spirit of Chicago.

--

I'd been asking everyone where I should buy a nice pair of hand-cobbled Italian shoes while I was there, because everyone said the shopping is great and I have a brown pair that need a friend in black. One lady actually had the temerity to say, "Nordstrom's" to which I replied with a whithering, "I am from Seattle." She supplied the implied part of my statement herself: "If you want to shop at Nordstrom's, you'll go to the flagship store." Exactly.

I stayed at the most beautiful hotel ever created, not that I saw a lot of it. After the talk, the first bar, loitering in the street yelling obscenities, the second bar / jazz club, and the all-night authentic taco place, we ended up stumbling in at 5:30AM. My bodyguard and I were like, "Damn... nice place... marble... gold elevators... zzzzz."

Saturday afternoon, while checking out, we did ask the world's greatest concierge where to go shopping for shoes. He was a man of immaculate grooming, English descent, impeccable manners, and strong opinions. He did not dither for a moment. He recommended an Italian-sounding shoe place on the Mile that sold, possibly, the greatest shoes in the world. Do not bother with the salespeople, he said, you must talk only to Alex. He is the manager, and he has a degree in orthotics from Northwest University. Not that I am saying salespeople are less-than-capable, sir, but you know, in fact they are. Nudge and wink.

You think I'm making this up, by I am not. In fact it went on for much longer. This man was so passionate about shoes his eyes glowed. He said he'd invited the manager of this particular establishment to speak at the annual convention of concierges, because he is renowned as an expert in his field.

I realized that my whole life up until that point had just been building up until the moment that I had discovered these shoes. Everything that had happened up until this afternoon - my career, the talk, my trip to Chicago - was merely to guide me to meet this man, so that I could possess the Platonic Ideal of shoes. I would have paid any amount for them.

--

The second bar we went to was the Green Mill, which is a Chicago institution and was apparently where Al Capone used to hang out. I can see why. It's a gorgeous old joint and if I lived in Chicago I'd go there every day. In Seattle bars close at 2AM by law; we got to Green Mill at 3AM and drank for hours more. The place was absolutely jumping. An ever-varying group of dudes came and went on-stage and would play some REAL jazz then come and sit with the rest of us and drink. I got the feeling if you knew how to blow you could just grab a sax and wander up and nobody would blink. The owner of the place was playing the baritone sax, which was pretty cool, although the drummer really stood out.

Almost every table was full with an incredible mix of people. Chicago is an integrated town. In Seattle we don't consider ourselves racist (that's for Southerners!), but the truth is we're comparitively lily-white, so it's easy to say, "Oh, of course we have no hate. Then again, we pretty much all look like ads for REI."

--

Adler Planetarium is in a campus of museums and stadiums and gorgeous architecture that makes me realize what a Real City is like. The only previous time I was in Chicago was to see the original King Tut exhibition at the same museum, which gives you an idea of my age and provided a beautiful bit of symmetry, I thought.

--

The talk itself was a lot of fun. There were people there who had flown in just to listen to a bunch of "independent" developers talk for a few hours. There was a Mac Users' group (with a cute president, who wore a mini-skirt for the event) from Purdue who had driven for six hours just to see us. I can't help but feel we must have been disappointing after that kind of build-up.

I got bought a lot of drinks afterwards by people whose names their own politeness has erased from my mind. The staff at Jak's Tap kept apologizing to me for not being ready for our event, because fifty or so of us showed up two hours late (there were no cabs at the planetarium, and we went an hour long anyways), right before the kitchen closed, and a lot of our people ended up hungry and/or angry. They thought that since I was wearing a nice shirt and had people buying me drinks I was the organizer.

I kept telling them that, no, drunkenbatman was the organizer, I just came to talk. They'd ask, "Who is this drunkenbatman?" And I'd look around and he'd be outside smoking or mashing on some strange girl, so I'd have to say, "Uh, well, I don't see him right now, but he's here..."

Finally Zoe (a very pretty redhead who looked like young Tori Amos without all the issues) decided this was all a bit too convenient, and declared, "You're drunkenbatman, aren't you?"

No baby, no I'm not. But good thing someone is.

Labels: ,

Wednesday, October 19

Contest Results and Prizes, Three Types

Going backwards in time:

— I recently offered a plush Delicious Monster sock monster if anyone could make my favorite macro a bit uglier and more efficient using the preprocessor, and although some said it was impossible, Toby Paterson posted the first solution that fits the specs. Mirko Viviani also posted a neat solution, and there was a bunch of neat discussion on the list, but I said the first solution that matched my criteria, so the prize goes to Toby. As an extra bonus, Toby, you can pick your favorite monster from the two below. (Also, e-mail me your physical address.)



— In July, I asked if anyone could find a class that, when subclassed, required you to call 'self = [super init]'. Ken Ferry wrote several cool programs to find classes that would return a different instance than the one you sent the -init message to, and although none of them were actually cases where calling 'self = [super init]' would help, it was a cool program so he won the $100 prize. He asked that his prize be commuted into a job interview, which I was only too happy to grant him, but a month later he (sadly) told me that Apple had hired him. (Apparently they aren't content just to simply hire away everyone who works for Delicious Monster, they also are now hiring people who I mention I might interview.)

— Way back in May, I posted a challenge in Grapher in Tiger, and young Padawan Lucas Newman responded with the answer. He won an upgrade to Delicious Library 2, but just recently also decided to upgrade that to becoming a remote contractor for Delicious Monster; he is now handling support and doing some programming for us under Drew Hamlin, our lead quality monster. We wish Lucas all the best in his assumed future career at Apple.

Labels:

Monday, October 17

Mac OS X Viruses: Results (sort of, except not really)

October 16th has come and gone, which means I'm virtually a year older and also that the deadline for my challenge to find a Mac OS X virus would have just passed, except I never actually started the dang contest.

I've been thinking about this a lot, because I consider honesty and honor to be a pretty big part of my gestalt, and I don't want to be viewed as a welcher. But, the truth is, almost nobody who responded to my original challenge read the whole dang post.

So, a couple of people found pointers to what might be viruses and posted about them and said, "Ok, do I win?" But, see, what I'd said was, "So, here's my plan. I'm not putting it into effect yet, but I'm soliciting comments, and if nobody can prove it's a bone-headed idea, I'll go ahead with it."

See, so it's not really fair to reward people who jumped in immediately after that and posted about viruses to try to claim the prize, because people who actually read the instructions were waiting for me to actually say, "Go," which I never really got a chance to say because most of the runners just jumped over the starting line and ran into the woods the second I suggested we might want to have a race. (To torture a metaphor.)

Also, seriously, I was just totally snowed by all the people responding. I mean, that was a lot of stuff. If anyone actually read all those responses, I apologize to you and your family.

--

But what of the meat? Was there any? Are there Mac OS X viruses? The answer is... well, maybe. I admit, at some point I had to stop reading everything people were writing in there, but I did see three nuggets that need followup.

Most likely is "Opener", which appears to fit all the requirements for being a real virus, and also apparently had a victim, as detailed in this article. There are a lot of different reports on what Opener does and how it might spread; I very much want to know if it really can infect a machine without the user actually giving it power explicitly (if inadvertently). It seems possible and even likely that there are different versions of "Opener" out there, since viruses tend to mutate as kiddies get hold of them and try to increase their power. So, while it was easy to write off "Opener" as not qualifying based on some descriptions of it, others seem more compelling. I want to look at this code myself.

An anonymous poster who possibly isn't a native English speaker but does seem to know a lot about malware also made comments about Opener ("When a password has been found on a remote machine by the brute force process, it installs his code by sshing various commands on the remote host."), which make it sound much like a virus, indeed, except it requires that the target machines have SSH turned on in order to be infected-- I do not know if SSH was on by default when Opener was making its rounds. I'd like to know this.

He also mentions, "Mail malicious bundle was created before your check point and it was designed to send a /tmp/xxx/virus file to all your friends each time mail.app receives your mail," which sounds like a virus, yes. Bears investigating.

And, finally, he mentions, "Do you know MacSerialJunkie? On the private cracks section there's a warning of a virus/worm spreading on a cracked version of ArchiCAD 9." My initial feeling is that it doesn't count if you knowingly download and run a cracked program; it's a lot like complaining that the horse you just stole isn't very fast.

So, I count three possibilities: the Mail bundle (How does it spread?), Opener (Does it really work over SSH? If so, did Mac OS X ship with SSH enabled at the time Opener was spreading?), and the cracked ArchiCAD 9 (Do we count this? If so, should we act surprised if we have sex with Pamela Anderson and end up with Hepatitis?).

Labels:

Sunday, October 9

Pimp, Pimp Thyself.

An anonymous comment on my last post got me thinking about my favorite macro (which I'd posted), and how it could be improved.

Here it is again, for reference:
DMCommonMacros.h
static inline BOOL IsEmpty(id thing) {
return thing == nil
|| ([thing respondsToSelector:@selector(length)]
&& [(NSData *)thing length] == 0)
|| ([thing respondsToSelector:@selector(count)]
&& [(NSArray *)thing count] == 0);
}

Now, I like this macro because I don't have to worry about the class of the Cocoa object I pass into it; it pretty much works for any primitive type that has an 'empty' state. (Notably not NSScanner, though.)

Some people didn't like it because it does an extra compare with nil, which is unnecessary under old versions of Cocoa because sending any method to nil returns nil, which is synonymous with the integer 0 or the value FALSE on all current implementations of Cocoa (but NOT synonymous with the float 0.0, DO NOT BE FOOLED).

However, Apple is attempting to deprecate this shortcut, because they can make the runtime more efficient if they know that no receivers are nil (c.f. the new GCC 4.0 compiler flags: GCC_NO_NIL_RECEIVERS or -fno-nil-receivers).

Since a nil comparison is REALLY quick: like, you can do a billion a second, since a zero comparison should be one instruction for a in-register variable, and you're going to have to load the variable up for the Objective-C call to -length or -count if you're not nil anyways. Also, if the receiver *is* nil, you never have to send the obj-c message at all, which saves you a function call, so this is actually faster.

So, I think the explicit comparison with nil is great. But, what about the -respondsToSelector:? It struck me that GNU C has had (for a LONG time) the ability to tell the type of a variable passed into a macro. Now, I'm not really good with this stuff (I've only used it once or twice), so I don't know if this is possible, but it seems like it MIGHT be possible to rewrite the macro so that if 'thing' is of a known type (NSArray or NSString or whatever) then we immediately check it for nil and then send it the CORRECT method (-count or -length or whatever, respectively), and we only fall back on the code I have if we pass in a variable that is truly of type 'id', which is pretty rare.

Possible? I'm not sure. But there's a handmade, one-of-a-kind Delicious Monster stuffed sock monster for the first person who does it and posts the (non-copyrighted) code here for everyone to use. (You can see an example of this artist's other work on the DrunkenBlog, where drunkenbatman has snaps of the cow she did for his logo.) Unlike the 'virus' post a couple days ago, this is not a trial balloon; contest starts RIGHT NOW and you get the sock monster if your code works, biggity-bam.

Labels:

Saturday, October 8

Pimp My Code, Interlude: Free Code

Ok, I wanted to share a file from my shared source directory, because I don't have very many things in my shared source directory. I'm very picky this time about what I consider "shared" -- I have to actually USE code in two different projects to consider it shared, not just think "Hmm, someday somebody may want to re-use this." Because, in truth, most of the crap people put into shared code directories is either too specific to really be shared, OR (more commonly) it's written in a general way but the particular app it was written for only tested some of the pathways, so it is essentially a bunch of buggy code that's not actually being used and is waiting to trip you up.

And, yes, if you'd like you can compound this mess by writing test cases for the code you don't use, to prove that it does what you think it might do even though nobody actually cares. And, yes, I've been paid to do this for people.

All that said, here's the single-most used file in my shared repository:

DMCommonMacros.h
static inline BOOL IsEmpty(id thing) {
return thing == nil
|| ([thing respondsToSelector:@selector(length)]
&& [(NSData *)thing length] == 0)
|| ([thing respondsToSelector:@selector(count)]
&& [(NSArray *)thing count] == 0);
}


Yup, that's the whole file. You WOULD NOT BELIEVE how often I use this macro. At least once per method, often twice or three times.

Essentially, if you're wondering if an NSString or NSData or NSAttributedString or NSArray or NSSet has actual useful data in it, this is your macro. Instead of checking things like "if (inputString == nil || [inputString length] == 0)" you just say, "if (IsEmpty(inputString))".

Seriously, this code may seem butt-obvious but you're going to find a million places in your code to use it. Pretty much every time I was just checking for nil, I now call IsEmpty(), because, hey, if someone just passed me in an empty stub of a string or array or whatever, that's still empty. I probably want to do the same thing.

This is especially important in Cocoa, because it's Apple's convention to return empty objects instead of 'nil' if the set of things you are looking for is empty; for instance, if you call -selectedObjects on a Cocoa instance that has no selection, you can expect to get an empty NSArray, not nil. (A glaring exception to this is the IOBluetooth toolkit, which sometimes returns nil instead of an empty array when you ask for a list of devices, which can break your code silently and badly if you're not expecting it. I filed a bug on this (RADAR #4093573) but, sadly, the engineers decided their behavior was correct, even though it Cocoa's stated conventions.)

Apple has also (mostly) cracked down on passing in 'nil' to Cocoa when you should pass in some empty object -- most of the time you'll get an exception thrown nowadays if you, say, set something to have a string value of 'nil' instead of @"" (c.f. NSMutableString documentation).

However, I use 'nil' all the time in my code, so at any given time, for any given method, I'm never really sure if empty input will be some empty object or 'nil'. Hence, IsEmpty(), which lets my code function perfectly without worrying what convention the caller is using.

Labels:

Monday, October 3

Pimp My Code, Part 5: Special Apple Sample Code Edition...

Ok, today we take a snippet of sample code from the great mothership herself, Apple, and we compare my style to theirs (or, at least, one of their programmers). Both of these routines work. You might feel one has advantages over the other, and I won't be peeved if you decide mine's not better.

Starting Position: Apple's Code from CoreData Sample Project
/* Change this path/code to point to your App's data store. */
- (NSString *)applicationSupportFolder {
NSString *applicationSupportFolder = nil;
FSRef foundRef;
OSErr err = FSFindFolder(kUserDomain, kApplicationSupportFolderType,
kDontCreateFolder, &foundRef);
if (err != noErr) {
NSRunAlertPanel(@"Alert", @"Can't find application support folder",
@"Quit", nil, nil);
[[NSApplication sharedApplication] terminate:self];
} else {
unsigned char path[1024];
FSRefMakePath(&foundRef, path, sizeof(path));
applicationSupportFolder = [NSString stringWithUTF8String:(char *)path];
applicationSupportFolder = [applicationSupportFolder
stringByAppendingPathComponent:@"ManyToManySQLTest"];
}
return applicationSupportFolder;
}
This code is from the sample application delegate class that is created when you create a new CoreData project in XCode that is NOT document-based.

First Pass: Doll It Up a Bit
// Change this path/code to point to your App's data store.
- (NSString *)applicationSupportFolder;
{
OSErr err = FSFindFolder(kUserDomain, kApplicationSupportFolderType,
kDontCreateFolder, &foundRef);
if (err != noErr) {
NSRunAlertPanel(NSLocalizedString(@"Cannot find folder", @"error title"),
NSLocalizedString(@"Your home folder appears to be missing its
""Application Support"" folder, which is required by many parts of
Mac OS X. You should restore this folder from backups or create a
new account and move your files over to it.", @"error text"), @"Quit",
nil, nil);
[[NSApplication sharedApplication] terminate:self];
}

FSRef foundRef;
unsigned char path[PATH_MAX];
FSRefMakePath(&foundRef, path, sizeof(path));
return [[NSString stringWithUTF8String:(char *)path]
stringByAppendingPathComponent:@"ManyToManySQLTest"];
}
For my first pass at this code I leave it semantically the same, but I apply my personal taste in spacing and variable declaration to it. You might find this makes it more clear or less clear; I think having the variables declared "just-in-time" reduces their scope, and leaves your head with less variables rattling around in it as you are analyzing the top part of the code. It's like, if someone's going to tell you a story, do they start out, "Ok, there's Susan, she's a cosmetologist, and Jenny, who works at a store, and this guy Phil, who's a sleaze from Nordstrom, and also this other person..." or do they start out, "Ok, so Susan is a cosmetologist, and she's working at the Nordstrom counter when this sleaze Phil walks up to her..."

I think, using the latter, it's easier to remember where each name fits into the larger picture.

Also, I change the error strings to suggest what the user should do to get out of the bind she's in. I mean, yes, it's going to be a rare condition. But if it's so rare we don't believe it'll ever happen, let's just exit the program silently if it does. We don't know that it's that rare, so we're duty-bound to have our error message not just describe the severity of the error, but also offer some suggestions on how to work around this problem.

Finally, I localized the errors, because, darn it, EVERY TIME you add an English string to a program, LOCALIZE IT. RIGHT THEN AND THERE. YOU WILL BE SORRY IF YOU WAIT UNTIL THE END. YOU WILL BREAK YOUR PROGRAM DOING LOCALIZATIONS RIGHT AS YOU ARE ABOUT TO SHIP.

Ok, so that's just a brief once-over. Can I do more? As it happens, yes. See that ugly FSRef and OSErr stuff? WTF? I never learned that in Cocoa class. Ah, because that's not part of Cocoa; it's Carbon. Snuck it right in there, didn't they. Of course, because Carbon is 22 years old, they've got to do some munging to get it to deal with modern strings and stuff, including hard-coding the maximum resolved pathlength of 1024 (I replaced it with its symbolic constant, hoping for a better day) . If only there were an easier way, using, like, strings and shit...

Second Pass: Replace Carbon with Cocoa
- (NSString *)applicationSupportFolder;
{
NSArray *applicationSupportDirectoryPaths =
NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory,
NSUserDomainMask, YES);
if ([applicationSupportDirectoryPaths count] == 0) {
NSRunAlertPanel(NSLocalizedString(@"Cannot find folder", @"error title"),
NSLocalizedString(@"Your home folder appears to be missing its
""Application Support"" folder, which is required by many parts of Mac
OS X. You should restore this folder from backups or create a new
account for yourself and move your personal files over to it.",
@"error text"), @"Quit", nil, nil);
[[NSApplication sharedApplication] terminate:self];
}

return [[applicationSupportDirectoryPaths objectAtIndex:0]
stringByAppendingPathComponent:[[NSProcessInfo processInfo]
processName]];
}
Check it out now. We've gone from 15 lines of code to 8, by my count. We've eliminated all calls to Carbon and any allocating of buffers ourselves. And, as an added bonus, we look up our process name automatically, so we don't have ask the user to change the code if she changes the program's name.

But, hey, does NSSearchPathForDirectoriesInDomains() actually check to see if the directory it just pointed us to physically exists, or is it just saying, "Here's the name of the directory, hope that works out for you"? Because, if it's the latter, there's really no reason for us to test the status of the return, because if Mac OS X has obviated the Application Support directory altogether then we're going to need to rewrite our app to get things working; there's nothing the user can do for us.

In that case, really, we should think about our method just looking like this:

(Optional) Third Pass: Screw Error Checking
- (NSString *)applicationSupportFolder;
{
return [[NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory,
NSUserDomainMask, YES) objectAtIndex:0]
stringByAppendingPathComponent:[[NSProcessInfo processInfo]
processName]];
}
And, voila, we have the one-line version.

This version will raise an exception if Mac OS X is ever changed in an incompatible way, and the paths returned is an empty array. I highly doubt that'll happen, but, if it does, well, an exception is not the worst way to go. I mean, seriously, Apple could change any method in Mac OS X in an incompatible way at any moment, and we can't write alternatives to each call or we end up solving the halting problem.

Some people might say, "Well, I like the previous one better, it's safer, etc," but remember, each line of code you have in your project is a line you have to understand, read past, support, and maintain. That previous version had two extra English strings it added to our localization dictionary, which means you'll be paying all your localizers to translate them.

My point is, those original 15 lines of code actually had a pretty big support footprint for what they actually did. (Especially when the name of the app was hard-coded in like that.) Our goal as programmers is to solve problems (big and small) in a way that causes the smallest support burden.

Labels:

Sunday, October 2

Talk Like a Pirate, Every Day.

If you know me at all, then you know that I'm a pompous, self-absorbed jerk. Oh, and that I like to talk like a pirate. Seriously, one of my status messages in iChat is "Talk like a pirate," and I spread the gospel of pirate-speak every day, not just every September 19th, like the rest of the civilized world (arrr, lubbers, all of 'em).

You may not know of my fondness for Tetris and its ilk (mmmm, delicious ilk) or my recently-kicked addiction to the on-line play of World of Warrrrrrcraft.

But imagine a game that combined all three of these great things! Now imagine that game ran on Mac OS X! Imagine that game is free! Ok, now imagine a thousand naked women screaming and throwing little pickles at you. Ok, now back up one, and go back to the game.

As much as I don't want to make a product-oriented blog (that's essentially like Consumer Reparrrrrts for über-yuppies) I cannot in good conscience keep a blog that purports to reflect my life without talking about Puzzle Pirates. Because, over the last four days since I've discovered it, pretty much my life has consisted of eating, sleeping, writing code, and playing Puzzle Pirates with me hearties, arrrrr. We arrrrr all, seriously, addicted. Like, a LOT. Like, I play it until 5am every night, except for the nights where I play it until 6am.

So, if you're like me (eg, you like pirates and puzzles and pickles), download this puppy now. What's the catch? If you want to play after the initial 30 days, or advance beyond a certain rank, you have to subscribe for, like, $10 a month. And, no, I'm not getting a referrarrrrrrl fee - hell, I doubt they could afford me. I just really love this game.

Me hearrrrrties and me be on the "Cobalt" ocean, based off the isle o' Dragon's Nest, so put yer free flop-pad there when ye sign up, and ye can come parrrrrty with us. (I'm "Sporeson," wearing rags o' green, so "/tell" me if ye joins up and wants to say hi.)

Saturday, October 1

Interlude... Cocoa Radio call for questions.

Cocoa Radio is going to do an interview of me, so if you have any questions you think would be cool for me to answer on the air, please submit 'em to them. Note that they're not reading my blog, so if you just post 'em here, that won't work. This is another of those "reading counts" moments, like when I said, "I'm considering posting a bounty," and then everyone asked if they'd won the bounty or not.

If, on the other hand, you aren't interested in my opinions on stuff and things, then you should not ask a question, nor listen to the interview. Nor should you read my blog, come to think of it.

Also, in general, if I give advice about women or money, do the exact opposite. In this way, everyone wins.

Labels: