A reader (@ramsch) writes to call my attention to his post on AAPL’s developer forums (unfortunately you need to log in as a developer to see it … scroll to the bottom of page 2 — reply #38 — if you can) describing an alternate, simpler approach to the directory monitoring code I discussed previously. His approach is interesting in and of itself, and as an introduction to Grand Central Dispatch. Today I provide some remarks on this code, and a working demo project, which you can download here.
GCD
Grand Central Dispatch is a newish technology introduced to iOS with the 4.0 release. GCD is a low-level concurrent execution API, and seems to be part of AAPL’s long-running “we hate user-level threads” campaign. Since it offers some of the same event-processing mechanisms as kqueue
, it can also be used to monitor and respond to changes in a directory’s contents.
Approach
The key to GCD-based directory monitoring is the dispatch_source_create
method, which “[c]reates a new dispatch source to monitor low-level system objects and automatically submit a handler block to a dispatch queue in response to events”. The “low-level system [object]” in this case is a directory file descriptor, and the “events” are writes. The “handler block” (you can read a bit about blocks here … they’re like closures for C) will trigger our existing updateFns
method. By using the dispatch_source_create
method, we can replace all the kqueue
and CFFileDescriptorContext
logic in the earlier demo with GCD-based code.
Code
The major changes are to the RootViewController
class definition, and to its startMonitor
and stopMonitor
methods. (The kqueueFired
method and KQCallback
static function are also removed.)
Here’s the bulk of the new code; hopefully the comments are sufficient explanation.
@interface RootViewController : UITableViewController
{
dispatch_source_t _src;
NSArray* fns;
}
- (void)startMonitor
{
// One ping only
if (_src != NULL) return;
// Fetch pathname of the directory to monitor
liveshareAppDelegate* delegate = (liveshareAppDelegate*) [[UIApplication sharedApplication] delegate];
NSString* docPath = [delegate applicationDocumentsDirectory];
if (!docPath) return;
// Open an event-only file descriptor associated with the directory
int dirFD = open([docPath fileSystemRepresentation], O_EVTONLY);
if (dirFD < 0) return;
// Get the main thread's serial dispatch queue (since we'll be updating the UI)
dispatch_queue_t queue = dispatch_get_main_queue();
if (!queue)
{
close(dirFD);
return;
}
// Create a dispatch source to monitor the directory for writes
_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, // Watch for certain events on the VNODE spec'd by the second (handle) argument
dirFD, // The handle to watch (the directory FD)
DISPATCH_VNODE_WRITE, // The events to watch for on the VNODE spec'd by handle (writes)
queue); // The queue to which the handler block will ultimately be dispatched
if (!_src)
{
close(dirFD);
return;
}
// Set the block to be submitted in response to an event
dispatch_source_set_event_handler(_src, ^{[self updateFns];});
// Set the block to be submitted in response to source cancellation
dispatch_source_set_cancel_handler(_src, ^{close(dirFD);});
// Unsuspend the source s.t. it will begin submitting blocks
dispatch_resume(_src);
}
- (void)stopMonitor
{
if (_src)
{
// Stop the source from submitting further blocks (and close the underlying FD)
dispatch_source_cancel(_src);
// Release the source
dispatch_release(_src);
_src = NULL;
}
}
Pingback: Things that were not immediately obvious to me » Blog Archive » GCD, Blocks, and Circularity
Pingback: Tweets that mention Things that were not immediately obvious to me » Blog Archive » Directory Monitoring and GCD -- Topsy.com