Archive for the 'Documentation' Category

The Green Checkmark of Acceptance

Saturday, March 13th, 2010

Here's how answers to a question on Stack Overflow appear to the questioner:

Every answer has, below the helpful/unhelpful buttons, a hollow checkmark button.

When the questioner clicks on one of those checkmarks, it marks the answer as the accepted answer to that question, and changes the checkmark from a gray stroke to a green fill.

Everybody else reading the question will see, below the questioner's name, an indication of how many of their questions have accepted answers. Today, for example, my questions have:

Peter Hosey
23.1k 2 14 28
67% accept rate

This indicates that I have accepted answers on four-sixths of my questions.

Sometimes, I see a comment like this semi-fictional example (written by me, based on several real examples I've seen) on a question whose author has a low or zero acceptance rate:

0% accept rate? You really should accept answers on your questions, or people may not answer any further questions from you.

This is a bad reason to accept answers.

The real reason to accept an answer is that you believe it's the correct answer.

Sometimes questioners choose bad answers (deprecated APIs, hacky solution, etc.). When that happens, it's a problem because it may lead future readers astray—they may think that this is the correct answer (because the questioner said so), without reading the other answers or the comments and finding out that this way sucks and/or there is a better one.

The same problem happens when a questioner accepts an answer because they think they have to, out of some sort of social obligation, rather than because they truly believe it is the correct answer. They may not have the correct answer yet, or there may not be a correct answer yet, but they feel like they have to accept something, so they accept the best answer they have, however good or bad it is, solely to raise that all-important number.

That sucks.

Questioners: About a day after asking a question, you should return to it, read all the answers, try them in descending order by votes, and accept the one that works and is the least hacky, for the benefit of other people who have the same question you asked. Take comments into account—something may not look hacky, but a comment may point out the hackiness.

And if there is no good answer, you don't need to accept anything. For the same reason (the benefit of future readers), you should leave the question open.

It's OK to have an acceptance rate that is below 100% or even low, as long as you are accepting answers that you find work and are non-hacky, on as many of your questions as you can. As long as you're making that effort, you're doing it right.

People who post comments like the one above: Why are you so desperate for karma? It's not like it's scarce or valuable. Net scores on answers are meaningful (usually), but your personal total, like mine, is next to meaningless. It's a reward, yes, but an empty one, so I don't see why you get all hurt when you perceive a risk that someone may not give it to you.

In summary: Don't worry about it. Accept correct answers, write correct answers, and don't worry about your acceptance rate or anyone else's.

An introduction to Cocoa and Cocoa Touch

Wednesday, February 3rd, 2010

If you know someone who's recently taken up programming the Mac or the iPhone (or both), please give them one (or both) of these links:

As a frequent answerer of questions on Stack Overflow, I see certain patterns—not just frequently-asked questions, but also frequently-unasked questions: Things that are biting the questioner that they don't know about.

Sometimes they're thinking about things the wrong way (e.g., using property list objects where they should have a model). Sometimes there is a route to a solution (“my app is slow” “run Instruments”), but they don't know about that route. Often, they'll use wrong terms that are right on another platform (like “member variable”), like a speaker using their native language's pronunciation rules when speaking a foreign one.

To help people over these speed bumps, I've composed an introduction for new Cocoa and Cocoa Touch programmers. (Both of the above links redirect to it.)

If any part of it doesn't help them (i.e., is unclear or wrong), I'd appreciate it if you'd let me know, either here or by email.

Blog posts vs. web pages

Monday, January 25th, 2010

Steve Smith says “Stop Blogging”:

I mean it. All of you people are writing fantastic, useful articles about code, methods, and technologies, but you’re putting them in blog posts — a date-based format that encourages us to leave things as they were, historically.

This got me to thinking about the difference between two of the tutorials I've published.

The pointers tutorial is a single web page. There's a date stamp, but it's way down at the bottom. The ASL series is nine blog posts.

In the three years since the previous version of the pointers tutorial, dozens of people emailed me to tell me about its major errors.

In the two years since I published the last of the ASL series (ignoring approximately a week afterward), nobody has told me of an inaccuracy in any of the posts.

There are a number of possible explanations for the ASL series receiving fewer (that is, no) corrections:

  • That its audience is narrower: Anyone who programs C has to deal with pointers. Only a very few Mac OS X programmers will ever touch ASL.
  • That it is less visible: One of these is linked from my home page and plenty of CS course reading lists (exhibits A, B, C, and D), and was linked for a while from the Wikipedia article on the C programming language; the other is practically unknown to anyone who wasn't subscribed to my blog at the time.
  • That I'm just that good. (Ha!)
  • That ASL hasn't changed at all since Leopard. (Ha!)

Smith writes from the perspective of the author and publisher, who must maintain a web page; he says that the author and publisher finds no (or not much of) such obligation for a blog post. I think the difference in my supply of corrections hints at a reader side to this, although, as shown above, my two examples are hardly comparable.

I have been meaning to move the ASL tutorial into a pointers-style web page at some point, although I don't know when. I may start receiving corrections then, which means I'll have to spend time to fix them. The flip side to that is that if I leave it as blog posts, I'll have that time for other things, but the posts will be consigned to periodically-increasing inaccuracy.

I expect to think more about Smith's suggestion.

There's also the merit of the word “blog”, which is wearing thin for me.

Pointers tutorial 1.3

Saturday, January 16th, 2010

At long last, a new version of Everything you need to know about pointers.

The most significant changes are long-overdue corrections regarding declarations of const pointers and the difference between arrays and pointers. You can—and, if you learned how to work with pointers from this tutorial, should—read all of the changes in the delta between 1.2 and 1.3.

The other way to install the Mac Reference Library

Wednesday, September 2nd, 2009

Starting with Xcode 3.2, the Xcode installer package no longer includes a local copy of any developer documentation. Instead, you have to either go to the website (which is what the redesigned Documentation Viewer window does) or download the documentation within Xcode.

Both solutions have their own problems. Reading the docs on the website can be frustrating if you're streaming or downloading something in the background. And downloading within Xcode is dubious if your internet connection is not super-fast or is flaky, because Xcode cannot resume downloads.

There is a third solution.

  1. In Xcode's Preferences, click on an info button at the right edge of the list of docsets, or right-click on the docset and choose “Get Info”.
  2. Select the row labeled “Feed URL”, and copy it.
  3. In a text editor or text field, paste the text, then edit it (it's the whole row, not just the value) down to the URL alone. There's a period (full stop) at the end of the text; it's not part of the URL, so make sure you delete that.
  4. Open the URL in a feed reader.
  5. On the most recent entry, copy the URL for the enclosure.
  6. Paste it into your download manager or browser Downloads window.

Assuming your download manager or browser supports resuming downloads, you now have a way to pause the download for any reason that may arise, and resume it from that point.

Of course, then you have to install what you downloaded. You're probably not used to seeing xar files (although you've seen more than you think—flat packages are based on them), so you may not know what to do with them.

  1. Pop open a terminal. cd over to where you downloaded the xar archive.
  2. pushd /Developer/Documentation/DocSets
  3. sudo xar -xf ~+1/filename.xar
  4. Once that finishes, delete (or back up) the xar file.

Xcode's Documentation Viewer window (or your browser) should suddenly be much quicker.

Git fetch weirdness explained

Sunday, August 30th, 2009

In another tussle with Git, I performed the following sequence of commands:

  1. cd mach_star-rentzsch
  2. git fetch (from GitHub)
  3. cd ../mach_star-boredzo
  4. git fetch rentzsch (in this context, my git-remote alias for ../mach_star-rentzsch)
  5. git merge rentzsch/master

Step 5 failed with “Already up-to-date”.

What? I just fetched! I should have new commits to merge in!

Nope. For one thing, this output from step 4:

From ../mach_star-rentzsch
 * [new branch]      master     -> rentzsch/master

seems to mean “OK, here's a new name for the master branch of that other repository, but we didn't actually bring in any commits”.

The reason it didn't bring in any commits is because git fetch apparently only fetches commits that are ancestors of the source repository's current HEAD. In English: git fetch cares what you have checked out in the source repository.

It's because I had fetched in step 2, and thereby not changed my HEAD, that step 4 did not see anything new to fetch. I don't know why it works that way, or why they consider it useful.

Anyway, the “correct” sequence of steps is not much different: git pull, not fetch, in step 2 above. Or use Mercurial, which I've found makes a lot more sense in general.

It's entirely possible that I've figured this out the wrong way, so take this entire explanation with a grain of salt.

Documentation supplement: System vs. Target in PackageMaker

Saturday, July 25th, 2009

The PackageMaker user guide doesn't explain the difference between “system” and “target” in PackageMaker's pop-up of Requirement criteria:

For example, “System OS Version” versus “Target OS Version”.

So now that I've figured it out, I'll fill in the gap for you.

  • System is the volume that the installing user is booted from.
  • Target is the volume that the Requirement is testing. (Your Requirements are applied for each volume.)

So if you want to make your Installer package installable to any bootable volume, make it installable to any volume and add a Requirement for Target OS Version. (Another method you may try is “File Exists on Target: /Library”.)

If, on the other hand, you want to make your Installer package installable to the Home folder, make it installable only to the Home folder and add a Requirement for System OS version.

How you can get this wrong

If you make your package installable to the Home folder but test the Target OS Version, your package is broken: It does not work for those of us who have our Home folder on a non-bootable volume (in my case, separate from two other, bootable volumes). You must use the System OS Version, and hope for the best.

If you make your package installable to any volume but test the System OS Version, your package is broken: The user will be able to install your software to a volume whose version of the OS cannot run it. You must use the Target OS Version.

As far as I know, there's no way to make a package that does both properly, since the choice of any volume, booted volume only, or Home only is per-package, not per-choice or per-contents.

Dramatic twist ending

The above is good if you're targeting Leopard. If you still support Tiger, there's a twist. (Obligatory video link.)

GrowlMail is a good example. As a Mail bundle, it requires a couple of user-default settings to work. That makes installing to /Library pointless, because the settings will only be set for the user who installed it, so it won't work for any other users on the system.

Leopard allows installing to ~, so that's easy: I use System OS Version, as I suggested above.

But Tiger's Installer can't install to ~. The same Installer package that works on Leopard does not work on Tiger (I even tested with earlier betas—it has never worked in any 1.1.6 beta). I don't know how nobody noticed this, not even our Tiger testers.

The Installer package for Tiger must target /Library, since I can't do the proper thing on that OS version, so I must make separate GrowlMail packages for Tiger and Leopard.

  • The Leopard package installs to ~/Library and uses System OS version, as I suggested above.
  • The Tiger package installs to /Library and uses both Target OS Version and System OS Version:
    • If the user is running on 10.5 or later (System OS Version ≥ 10.5.0), the package tells them to use the other package. (The other package has a similar check.)
    • If a destination volume does not have 10.4 or later installed on it (Target OS Version < 10.4.0), the package tells them they can't install there.

This is what you'll find in Growl 1.1.6b4 and 1.1.6 final. It'll go away in 1.2, since we're dropping Tiger then.

My documentation viewer

Wednesday, July 15th, 2009

This is what I use to view Apple's documentation:

My web browser.
QuickTime/H.264, 960×538, 1.3 MiB

The application is OmniWeb. I have a series of entry points bookmarked in the (hidden) Favorites bar:

(Someday, Carbon will perish from the list, the ones after it will move up, and another framework—probably either QTKit or Core Animation—will become the new ⌘8.)

And yes, those are all file: links.* Your web browser is perfectly capable of displaying web pages stored locally, and that's all the Apple documentation is: locally-stored web pages.

With this arrangement, I can get to the reference information I'm looking for faster, and I can have multiple references (even multiple definitions) open at once because OmniWeb supports tabbed browsing.

Here are some other pages worth bookmarking:

You can use these and other bookmarks with a nice feature of OmniWeb which has also, more recently, appeared in Google Chrome: You can type any substrings from your bookmarks' names and URLs into the address bar, separated by whitespace, and it will know what you mean. So, for example, I can type “kt kit”, and OmniWeb knows I mean “QuickTime Kit”; I simply hit return, and it takes me to that framework reference.

UPDATE 2009-09-07: Updated links to Snow Leopard's docset name (where possible).

* On Leopard, change the docset name to com.apple.ADC_Reference_Library.CoreReference.docset.

Singletons in Cocoa: Doing them wrong

Wednesday, June 17th, 2009

A singleton is a simple concept: It's an object that you can only have one of. Being that simple, you may think it's hard to get wrong.

If you do, I welcome you to the wonderful world of programming.

The Cocoa Fundamentals Guide is necessary reading for all Cocoa programmers. Even so, nobody's perfect, and the CFG's authors are no exception.

On Twitter last night, a mistake of theirs came to light when someone named Nick tweeted about how hard it is to make a singleton in Cocoa. Nick cited an example from the CFG; Chris Hanson responded that “that example does WAY too much work”, and Bill Bumgarner added that the example is “ridiculous”. They're both right.

Doing it wrong.

The example, in order to “ensure singleton behavior”, overrides four basic Foundation methods:

  • retain
  • release
  • autorelease
  • retainCount

In the override implementations, retain, release, and autorelease are all no-ops. release does exactly nothing; the other two do nothing but return self. The override implementation of retainCount unconditionally returns UINT_MAX.

Before I proceed any further, I'll give you a caution about singletons. You generally do not need to make your object a singleton at all. Even if your application has a single-window interface, with only one of each controller, you probably don't need to enforce that. Just don't create more than one. Then, if you do create more than one, you have a bug.

Let's digress for a moment. Imagine if you allowed it. Imagine that you allow, say, your app delegate to create more than one of your root controller object. Your app delegate will then proceed to set up both twin objects, and they both respond to such things as notifications.

Bad, right? Now let's say you “fix” this by making your root controller a singleton.

Your app delegate is still trying to create two root controllers. It's still trying to set up two root controllers, and as a result of the two set-up messages, the controller is signed up twice for notifications.

But now you really only have one root controller. Your app delegate creates the first one, then gets it again when it tries to create the second. All according to plan so far. Then your app delegate sets up the first object—and then it sets it up again, again thinking that it's talking to a second object. Even worse, because the object is now signed up twice for notifications (once per set-up message), every notification response happens twice.

You now have only one root controller, but you didn't fix the bug, which wasn't in the controller at all, but in the app delegate. To fix the bug, you must fix the app delegate; you don't need a singleton for this at all.

OK, digression over. Singletons are bad. Avoid them. If you have a lot of them, rip most of them out. (Dave Dribin has bookmarked a lot of other good cases against singletons, and BJ Homer points out that they aren't all bad. More on BJ's post later.)

Back to the singleton at hand.

First, let's look at retainCount. A comment explains that UINT_MAX “denotes an object that cannot be released”, implying that the framework itself considers UINT_MAX a special value.

This is actually correct, although I originally thought (and wrote, in an earlier version of this post) that it was bogus. The documentation for retainCount explicitly says that objects whose release method does nothing should return UINT_MAX.

Next on the hit list: autorelease. This is just mostly pointless. autorelease means nothing more than “send this a release message later”. If release does nothing, then autorelease becomes “do nothing later”. All this override does is move the nothing up to now. A performance optimization, perhaps, but I'd say it's premature.

And now we come to the real villains: retain and release.

First off, you shouldn't retain a singleton anyway. I can't imagine why you would do this, except incidentally, by making it a timer target or something.

But even if you do think of a reason to retain a singleton, you still need to balance retain and release. If you retain without releasing afterward, you are under-releasing the object. If you release without retaining previously, you are over-releasing the object. These two statements have no exceptions.

If you break retain and release, then you're able to over-retain or over-release (or even both!) the object with impunity. Your app no longer crashes, but you didn't really fix the problem; you're just getting away with it. You're still trying to over-retain and/or over-release an object.

The Cocoa Fundamentals Guide's primary audience is new Cocoa developers. Those who have never used a retain-counting system before may very well over-release the singleton, and Apple's example singleton implementation hides that bug from them. That's bad; every Cocoa programmer should know how to recognize an over-release crash, and breaking retain and release denies the reader an opportunity to learn that. I've filed a documentation bug report about the example.

Also, a rebuttal

BJ Homer responds at a different angle to last night's flurry of tweets.

First, though, we need to get a definition straight. A singleton is a class of which there should only be one instance in any given process. There are actually very few singleton classes in the Cocoa framework including NSDocumentController and NSFontManager. You cannot create more than one of these objects; …

This is true. A singleton object is an object that your app can only have one of. There are a couple of different ways to do this:

… if you try to call [[NSDocumentController alloc] init], you'll get back the exact same object as you do when you call [NSDocument sharedDocumentController], no matter how many times you call alloc and init. NSApplication is arguably a singleton as well; you can alloc another one, but you'll get an assertion failure when you call init.

Another way is to simply implement the sharedFramistan method, and leave allocWithZone: and init alone. I suspect that this is common, since it's the easiest way, but BJ is right that it isn't a true singleton, since it allows multiple instances.

Where BJ goes wrong is in his defense of the retain and release overrides:

Consider the following example:

MyFloozit *floozit1 = [[MyFloozit alloc] init];
[floozit1 doSomething];
[floozit1 release];

MyFloozit *floozit2 = [[MyFloozit alloc] init]; // MyFloozit is a singleton, so this should be the same object as floozit1
[floozit2 doSomething]; // CRASH HERE
[floozit2 release];

We'll leave aside the problem that you probably should be using sharedFloozit to obtain the singleton instance of MyFloozit.

When floozit1 is set, a new MyFloozit is allocated, and a static MyFloozit pointer is set. When floozit1 is released, that static pointer is still pointing to the old instance. As a result, when we try to set floozit2 (or when anyone else tries to call [MyFloozit sharedFloozit]), we get back a pointer to that same instance. The one that has been dealloc-ed.

BJ is missing something else the CFG says, which he even quoted later on:

Situations could arise where you want a singleton instance (created and controlled by the class factory method) but also have the ability to create other instances as needed through allocation and initialization. …

(Emphasis added by me.)

A singleton object owns itself. As such, it retains itself. As such, it should never die, because it always has at least one owner—and that's without breaking retain and release. If it does die, it's because something over-released it; later, something that was using it will crash, which lets you know that you have a bug. This is a good thing, because now you can fix the bug.

Each of the release messages in BJ's example balances an alloc message above it. That alloc message may actually return an existing object, but we're expecting to own a new object. Therefore, the singleton's allocWithZone: should implicitly retain the existing object.

There is no good reason to override retain and release. Don't do it. This also goes for autorelease. And, since you never override release, you also never need to override retainCount.

Doing it right

So, having thoroughly dismantled the Apple documentation's poor implementation of a singleton, let's look at the correct way to do it.

Let's go through the requirements:

  • The One True Instance is the only instance. (If you deliberately allow multiple instances, I call this a “multipleton”. I'll leave that as an exercise for the reader, and concentrate on the true singleton here.)
  • There is a sharedFramistan method. It tests whether the One True Instance exists; if not, it creates the object and remembers it in file-scope static storage. Then it returns the One True Instance.
  • We'll allow going through alloc and init, and return the same instance. We'll do this in allocWithZone:, as Apple did. We'll also need to make sure init doesn't do its work twice on the same object.
@interface PRHEmptySingleton: NSObject
{
}

+ (id) sharedFramistan;

@end
static PRHEmptySingleton *sharedInstance = nil;

@implementation PRHEmptySingleton

+ (void) initialize {
    if (!sharedInstance) {
        //init will assign sharedInstance for us.
        [[self alloc] init];
    }
}

+ (id) sharedFramistan {
    //Already set by +initialize.
    return sharedInstance;
}

+ (id) allocWithZone:(NSZone *)zone {
    //Usually already set by +initialize.
    if (sharedInstance) {
        //The caller expects to receive a new object, so implicitly retain it to balance out the caller's eventual release message.
        return [sharedInstance retain];
    } else {
        //When not already set, +initialize is our caller—it's creating the shared instance. Let this go through.
        return [super allocWithZone:zone];
    }
}

- (id) init {
    //If sharedInstance is nil, +initialize is our caller, so initialize the instance.
    //Conversely, if it is not nil, release this instance (if it isn't the shared instance) and return the shared instance.
    if (!sharedInstance) {
        if ((self = [super init])) {
            //Initialize the instance here.
        }

        //Assign sharedInstance here so that we don't end up with multiple instances if a caller calls +alloc/-init without going through +sharedInstance.
        //This isn't foolproof, however (especially if you involve threads). The only correct way to get an instance of a singleton is through the +sharedInstance method.
        sharedInstance = self;
    } else if (self != sharedInstance) {
        [self release];
        self = sharedInstance;
    }

    return self;
}

@end

Now, your tests:

  1. sharedFramistan always returns the same object.
  2. alloc/init always produce the same object (which, itself, is the same object as sharedFramistan).
  3. alloc/init will not return an object confused by multiple init messages.
  4. Over-releasing causes a crash.
  5. Keeping alloc/allocWithZone:/retain balanced with release never causes a crash.
  6. If [super init] returns a different object, alloc/init won't break.

Apple's example fails test 4 (and 6, for that matter). BJ doesn't show an implementation for his example, but according to his own description of its behavior, it fails test 5. My example above passes all six tests.

Take-aways

Hiding bugs is bad. Even worse is giving code that can hide a bug to new Cocoa programmers who could really use practice in detecting and fixing that kind of bug.

If you really need to implement a singleton, there is a right way to do it. The way currently shown in the Cocoa Fundamentals Guide isn't it.

Don't change the behavior of retain, release, retainCount, or autorelease. Ever.

UPDATE 2009-09-04: Removed hasInited instance variable after Christiaan Hofman pointed out its redundancy in the comments.

UPDATE 2009-09-19: Moved assignment of the sharedInstance static variable from +initialize to -init, as suggested by Uli Kusterer.

How to make hg merge, hg resolve use FileMerge

Wednesday, April 22nd, 2009

Put this in your ~/.hgrc file:

[merge-tools]
filemerge.executable=opendiff
filemerge.args=$other $local -ancestor $base -merge $output
filemerge.gui=True

(Based on the original description of and Matt Mackall's comment on a Mercurial bug about merging.)

Manpage Monday: PlistBuddy(8)

Monday, April 13th, 2009

PlistBuddy(8) is a command-line tool for editing property-list files, with a deeper reach than plutil defaults:

Entries consist of property key names delimited by colons. Array items are specified by a zero-based integer index. Examples:

  :CFBundleShortVersionString
  :CFBundleDocumentTypes:2:CFBundleTypeExtensions

Jonathan “Wolf” Rentzsch, in his del.icio.us bookmark for the manpage, says that PlistBuddy is “always at /usr/libexec/PlistBuddy on modern systems”.

Manpage Monday: copyfile(3)

Monday, March 30th, 2009

copyfile(3) is an API for copying and moving files within the file-system:

DESCRIPTION

These functions are used to copy a file's data and/or metadata. (Metadata consists of permissions, extended attributes, access control lists, and so forth.)

The copyfile() function can copy the named from file to the named to file; the fcopyfile() function does the same, but using the file descriptors of already-opened files.

The copyfile() and fcopyfile() functions can also have their behavior modified by the following flags:

COPYFILE_CHECK

Return a bitmask (corresponding to the flags argument) indicating which contents would be copied; no data are actually copied. (E.g., if flags was set to COPYFILE_CHECK|COPYFILE_METADATA, and the from file had extended attributes but no ACLs, the return value would be COPYFILE_XATTR.)

COPYFILE_PACK

Serialize the from file. The to file is an AppleDouble-format file.

COPYFILE_UNPACK

Unserialize the from file. The from file is an AppleDouble-format file; the to file will have the extended attributes, ACLs, resource fork, and FinderInfo data from the to file, regardless of the flags argument passed in.

File Manager also has APIs for copying files, in both asynchronous and synchronous flavors. Those APIs don't provide as much control over management of metadata, but they do offer asynchronous operation, whereas the copyfile(3) APIs appear to be synchronous.

And, of course, I should mention NSWorkspace operations, which you use with the performFileOperation:source:destination:files:tag: method. Unlike the other two, this API has been around since 10.0. On the other hand, like copyfile(3), it's synchronous only.

Manpage Monday: CC_SHA(3cc)

Monday, February 16th, 2009

The Common Crypto library in Mac OS X 10.4 and later provides simple APIs for five SHA algorithms:

CC_SHA1() computes the SHA-1 message digest of the len bytes at data and places it in md (which must have space for CC_SHA1_DIGEST_LENGTH == 20 bytes of output). It returns the md pointer.

CC_SHA1_Init() initializes a CC_SHA1_CTX structure.

CC_SHA1_Update() can be called repeatedly with chunks of the message to be hashed (len bytes at data).

CC_SHA1_Final() places the message digest in md, which must have space for CC_SHA1_DIGEST_LENGTH == 20 bytes of output, and erases the CC_SHA1_CTX.

The successor versions of SHA-1, SHA-2, are also implemented for hash bit lengths of 224, 256, 384, and 512. The functions to call to invoke the larger hash-size versions of the algorithms include the hash size as part of the function names: …

RETURN VALUES

All routines return 1 except for the one-shot routines ( CC_SHA1(), etc.), which return the pointer passed in via the md parameter.

How ClickToFlash works

Thursday, January 29th, 2009

Speaking of everybody's favorite WebKit plug-in, here's how it works. This should help you understand how it fails on some sites, and maybe aid you in contributing to its development.

First off, it is a WebKit plug-in; it's written in Cocoa and uses WebKit's own plug-in API. It does not use the Netscape plug-in API.

A WebKit plug-in declares a list of MIME media types in its Info.plist bundle. It does this by way of a dictionary of dictionaries:

<key>WebPluginMIMETypes</key>
<dict>
    <key>application/x-shockwave-flash</key>
    <dict>
        <key>WebPluginTypeDescription</key>
        <string>ClickToFlash</string>
    </dict>
</dict>

WebKit only loads a WebKit plug-in when it first encounters some content that it needs the plug-in for. It uses these Info.plist dictionaries to know which plug-in it needs to load. So, in the case of ClickToFlash, it only loads ClickToFlash when it encounters something that is of type application/x-shockwave-flash.

Here's the brilliant part. Adobe's Flash Player plug-in actually declares* two MIME media types: application/x-shockwave-flash and application/futuresplash.

You will notice that ClickToFlash only declares one of these.

As it turns out, everybody only uses application/x-shockwave-flash. ClickToFlash exploits this.

When you click on the ClickToFlash view, it modifies the object or embed element that the view represents, changing its type attribute to the other type—the one ClickToFlash doesn't declare; the one no webpages actually use. WebKit notices this change and looks again for a plug-in to handle the movie. This time, it comes up with only one handler: the real Flash Player plug-in.

There are several reasons why a site may not work with ClickToFlash. I suspect one reason is that they try to interact with the movie via JavaScript; ClickToFlash doesn't export a script object and wouldn't be able to communicate with the real Flash Player anyway. The script finds itself talking to a wall, and breakage happens.

So now that you know how ClickToFlash works, maybe you can help fix its bugs?


* Flash Player doesn't declare its MIME types in its Info.plist; it declares them in a resource file, in 'STR#' resource 128. Thanks to WebKit developer Mark Rowe for reminding me to look for a resource.

My CocoaHeads unit testing presentation

Monday, January 5th, 2009

One of the things that's been keeping me busy lately (too busy, in fact, to keep up Framework Friday and Manpage Monday—sorry about that, and I'll get back to them when I can) is preparing a video based on the presentation I gave at CocoaHeads last month.

I have now finished it. Its title is simply “Unit testing”.

The subject is unit testing for applications written in Cocoa to run on the Mac. I'm not an iPhone developer, so the video is only generally applicable to iPhone development (as much as it is to any other platform); for specific information about testing on the iPhone, see Colin's blog post.

I corrected a few things that I got wrong in the original live presentation, and added a few new slides. Other than that, it's basically the same.

The video is 13+½ minutes long (strangely, about half the length of the original live presentation), and is available in high-definition. You can even download the original file I uploaded, in case you're so inclined; it uses the lossless Animation codec for the video track, and is 720p and 135.7 MiB.

I used several Creative Commons photographs in the slides. Here are the links back to them:

In other news, I love Keynote's Instant Alpha feature.

Manpage Monday: notify(3)

Monday, December 8th, 2008

notify(3) is a simple API that allows processes to poke other processes, and to be poked.

The API is similar to NSDistributedNotificationCenter, except that it's not object-oriented, it's not based on Foundation or CF, and it doesn't allow you to attach a property list to the notification.

You register in one of four ways:

notify_register_check
Sign up under a name, and get a registration token.
notify_register_signal
Have the system send your process a signal when another process posts a notification with a particular name. Also get a registration token.
notify_register_mach_port
Have the system send your process a message on a Mach port when another process posts a notification with a particular name. Also get a registration token.
notify_register_file_descriptor
Get a registration token, and have the system write that token to a file descriptor when another process posts a notification with a particular name.

Each of the last two functions will create the Mach port or file descriptor for you, unless you tell it to use one you already have. You do that by passing NOTIFY_REUSE.

All four functions give you a registration token (the first one does nothing else), which is simply a signed 32-bit integer. You can then use notify_check to poll that token to see whether any processes have posted a notification to it since the last time you checked. (I don't know how the other notification methods affect this—e.g., whether notify_check will ever return true if you got the token from notify_register_signal.)

You free the token using notify_cancel. This also closes any Mach ports or file descriptors you have associated with it. There is no way to disassociate them from the token or to cancel the token without closing the associated resources.

On the sending side, the API provides one function: notify_post. It takes one argument, which is the notification name; this is the same name that any receiving processes passed to their chosen notify_register_whatever function.

Leopard adds a new feature: you can associate a shared 64-bit number with a name using notify_set_state, and retrieve that number using notify_get_state. I suggest that you only use notify_set_state in a single process, as I can imagine race conditions occurring if two processes try to set state under the same name. If you need bidirectional communication, use two notification names (or a different means of communication, such as sockets or Mach ports).

Apple introduced the rest of the notify(3) API in Panther.

Updates to “How to work with a bound-to array”

Wednesday, December 3rd, 2008

Following an email conversation with Dave Dribin, I've updated my earlier post on how to work with a bound-to array.

The most critical update, and the reason for a new blog post to announce the update, is that you should not currently implement addObject: and removeObject: for an array property. KVO has a bug where it treats these as set accessors, even if the property is an array. This will result in crashes if you observe your object using plain KVO.

The workaround is to use indexed accessors:

[self insertObject:newFramistan inFramistansAtIndex:[self countOfFramistans]];

This is a little uglier than addFramistansObject:, but it doesn't crash.

The other update is that you need to implement both accessors in a pair. For example, for insertObject:inKeyAtIndex: to work, you must also implement removeObjectFromKeyAtIndex: (and vice versa). Otherwise, KVO won't override the one you did implement, so you won't get notifications for it.

I've filed bugs for both of these, and I've linked to both bugs from the older post.

Framework Friday postponed

Wednesday, November 26th, 2008

The framework I picked turned out to be buggier than usual. Filing those bugs, plus some other things I have on my plate, means I won't get done anytime close to last Friday.

So, last week's Framework Friday post will appear on the next Framework Friday instead.

Sorry.

How to work with a bound-to array

Wednesday, November 26th, 2008

You have a model object with an array property:

@property NSArray *framistans;

You also have an array controller whose contentArray binding is bound to this property.

How do you add an object to the array?

Wrong #1: Direct manipulation

[framistans addObject:newFramistan];

The array controller will not notice this change. Worse, the value of your array no longer matches the value your array controller last saw. (For one thing, your array is now one object longer.)

Wrong #2: Use the array controller's add: action

[arrayController add:nil];

This works, but now you need to go looking for that object. The proper way would be to get the binding info for the array controller's contentArray binding, then ask the bound-to object for its array, and get the lastObject of that array.

More likely, you'll hard-code knowledge of which object and which property it's bound to. Good luck when you change the binding!

Oh, and in a model object, this breaks the separation between your model and everything else. Your model should know nothing of your UI, so that you can replace the UI wholesale if ever you want to (for example, if you make a CLI or web-based version of your app) and keep the same model.

Wrong #3: Add the object to the array controller's content array

newFramistan = [arrayController newObject];
[arrayController addObject:newFramistan];

This works, and at least you already have the object on hand. But you're still giving your model knowledge of the array controller, so you're still breaking separation.

Almost right: Add it to the mutableArrayValue for your property

[[self mutableArrayValueForKey:@"framistans"] addObject:newFramistan];

This works, and it's clean.

The only problem with it is that it's too heavyweight: you're creating this proxy object just to mutate a property. The most appropriate use for mutableArrayValueForKey: is if you want to pass a mutable array to another object, and you want that array to actually be a property of another object. This is a very rare case—I've never needed to do it.

For this problem, there is a better solution.

The Right Way: Use your accessor

UPDATE 2008-12-02: Don't do this. See the section I added below.

[self addFramistansObject:newFramistan];

Look at that.

Beautiful, isn't it?

It's almost as short as the direct array access, and it does the Right Thing with KVO. Your array controller will find out about the change without you having to tie knowledge of the array controller into your model.

We should also look at the accessor:

- (void) addFramistansObject:(Framistan *)newFramistan {
    [framistans addObject:newFramistan];
}

Also short, and also beautiful. (Yes, that is the complete definition of the method. I'm not eliding anything.) And there are no separation violations here, either: It's pure model.

When you bind the array controller to your object, KVO wraps your object and all its KVC-compliant accessors, including this method. Its implementation performs the proper KVO notifications around a call to your implementation, which means you don't have to do any KVO work at all.

UPDATE 2008-11-30: Note that you must also implement removeKeyObject:; if you don't, addKeyObject: will not post the KVO notifications. You must implement both. Thanks to Dave Dribin, who emailed me about this.

ADDED 2008-12-02: The current Right Way: Use indexed accessors

KVO does not work correctly with addKeyObject: and removeKeyObject: for array properties.

The problem is that it treats those methods as set accessors and posts set-mutation notifications, regardless of the fact that it's an array property. This doesn't seem to cause a problem with Bindings (as of 10.5.5), but when I tried observing the property using KVO directly, I got a crash every time, as it tried to send NSSet messages to my NSArray.

Instead, you'll need to use indexed accessors:

[self insertObject:newFramistan inFramistansAtIndex:[self countOfFramistans]];

As with addObject: and removeObject:, you must implement both of the pair. Here's what removal looks like:

[self removeObjectFromFramistansAtIndex:[self indexOfObjectInFramistans:framistanToRemove]];

indexOfObjectInKey isn't actually something KVC looks for, but it fits the use case and beats getting the entire array (which generally means copying it) just to find the index of one object. And you have to implement all these methods anyway.

Note that you should not implement addKeyObject: and removeKeyObject: at all for an array property, even to call the indexed methods. That's because KVO always posts its erroneous set-mutation notifications around your addKeyObject: and removeKeyObject: methods, regardless of how you implemented them.

Thanks again to Dave Dribin, as most of this came up in the same email thread.

Further reading

Manpage Monday: afconvert(1)

Monday, November 24th, 2008

afconvert is a command-line utility that uses Core Audio to convert audio files from one format to another.

The manpage is actually pretty sparse (like the rest of the Core Audio documentation); you're better off reading the command-line help:

% afconvert -h

A simple example is to convert an AIFF file to AAC in an MPEG-4 file:

% afconvert -f 'm4af' -d 'aac ' -b 98304 Recording.aiff

The resulting file is 96 kbps (98,304 bps) and is named “Recording.m4a”.

UPDATE 2008-11-26: Corrected permalink and title to include the section number.