Incremental Writing in Snow Leopard's NSFileWrapper 2009/08/30

In my post about some of my favorite Snow Leopard features, I mentioned incremental writing in NSFileManager. This was a typo, of course, and I meant NSFileWrapper. As penance, I thought I’d go into a bit more detail.

First, a quick test for you to try under 10.6:

  • Create a new RTFD document in TextEdit.
  • Drag in some large file, making sure to select “Copy” when prompted.
  • Save it. Wait for the spinning wait cursor…
  • Edit the text a bit.
  • Save again and OMG it is already done!

If you try this under 10.5, the second save will be just as slow as the first. Worse, this pause will occur during autosave, disrupting your editing! Under 10.6, though, the file wrapper can reuse the parts of the on-disk representation that have not changed.

Let’s look a bit deeper. Having a such a document already set up, open Instruments, and select the “File Activity” template. Attach to TextEdit and sample an iteration of the edit/save cycle. Selecting the “Directory I/O” Instrument, you’ll see something like this line:


Note the selected line above: NSFileWrapper is making a call to the BSD function link. When creating the new directory tree representing the updated wrapper, NSFileWrapper creates a second reference to the unmodified attachment and then writes only the modified “TXT.rtf” file! This “hard link” is a long-time Unix filesystem feature that allows the same file to be referenced by more than one directory entry. In recent versions of HFS+, in support of Time Machine, even a directory may be referenced by more than one parent (though you should not be too nosy about this).

But “Wait!”, you say. “What if some clever user has edited that attachment externally?” Say I were to pop into Terminal replace the contents of the embedded movie with something like?

cat > "file wrapper test.rtf/"

If you try this, then very next save will be slower as NSFileWrapper notices you’ve changed the embedded attachment and it can no longer re-use the existing disk representation. Instead it rewrites the movie from its in-memory cache, getting you back to the state it expects.

As an aside, editing of the internal members of a NSFileWrapper is the sort of edge case that keeps developers up at night. There are a variety of approaches to this that have tradeoffs between respecting the user’s edits and providing robust predictable behavior. If you’d like to do something nicer, in your app, looking at the documentation for -[NSFileWrapper needsToBeUpdatedFromPath:] and -updateFromPath: would be a good place to start.

For more information, see File Wrapper Writing Options and the “New Support for Incremental Writing in NSFileWrapper” section of the Application Kit Release Notes.

blog comments powered by Disqus