Pugetworks
Seattle Software Developers

blog-archive

Pugetworks Blog Archive!

Introspection and Objective C

I've been using Objective C for a few years now and what I enjoy about the language is its dynamic features.  They're surprisingly clean and effective given that they're essentially just smalltalk-isms grafted on to straight C. The more powerful introspective features are unfortunately only available when accessing Objective C's runtime directly, which makes them impractical for tasks considered run-of-the-mill when using higher level languages.  You can nonetheless do some pretty neat things using only conventional Objective C.  For example, Objective C's dynamic method querying and invocation have pretty low friction and I've come to rely on them fairly regularly.

As one example, consider the problem of traversing a hierarchy of objects while simultaneously conducting operations on those objects.  The naive approach would be to simply combine the traversal logic with the logic needed for each separate operation.  The problem of course is that you wind up with excessive code duplication when you repeatedly copy and paste the traversal code into each operation's implementation.

A real world instance of this problem I ran into was a situation with an iPhone app where I needed to dismiss the keyboard, if it was being displayed, when the user tapped the Save button on a form view.  What I needed to do was navigate the entire tree of GUI objects contained in the view and, for each UITextField, call resignFirstResponder in order to dismiss the keyboard1.  That's a lot of boilerplate when ultimately all I wanted to do was call a method on an object.  Worse, it's boilerplate that I would have needed to repeat if, for example, I ever needed to determine which UI elements overlap a given region, or if I ever needed to reset the content offset of every UIScrollView except the UIScrollView with first responder status, or whatever.

So the following function, recursivelyApplySelectorToSubviews, solves this problem for me by separating tree traversal from application specific logic and does so such that it's reusable.  It works like this: recursivelyApplySelectorToSubviews is given the root UIView of a hierarchy of nested UIViews.  It traverses the hierarchy and for each UIView, the function pointed to by predicateToApply is invoked2.  If YES is returned then the selector selectorToApply is invoked on the UIView, passing in selectorParam.


typedef BOOL(*PredicateFunc)(id);

//
// Traverses the graph given by root.  For each node, predicateToApply is
// invoked and passed the node.  If YES is returned then selectorToApply is
// invoked on the node in conjunction with selectorParam.
void recursivelyApplySelectorToNodes(id root,
                                     SEL fetchChildrenSelector, 
                                     SEL selectorToApply,
                                     NSObject* selectorParam,
                                     PredicateFunc predicateToApply) {

    NSMutableArray* queue = [[NSMutableArray alloc] init];
    NSMutableArray* visited = [[NSMutableArray alloc] init];
    [queue addObject:root];

    while ([queue count]) {
        // dequeue next
        id node = [queue objectAtIndex:0];
        [queue removeObject:node];
        if ([visited containsObject:node]) continue;
        [visited addObject:node];

        // enqueue children
        NSArray* children = [root performSelector:fetchChildrenSelector withObject:nil];
        for (id c in children) {
            [queue addObject:c];
        }

        if (predicateToApply && predicateToApply(node)) {
            if (selectorToApply && [node respondsToSelector:selectorToApply]) {
                [node performSelector:selectorToApply withObject:selectorParam];
            }
        }
    }

    [visited release];
    [queue release];
}

// Wrapper around recursivelyApplySelectorToNodes that's more convenient for use
// with UI hierarchies composed of UIViews.
void recursivelyApplySelectorToSubviews(UIView* rootView,
                                        SEL selectorToApply,
                                        NSObject* selectorParam,
                                        PredicateFunc predicateToApply) {

    return recursivelyApplySelectorToNodes(
        rootView, @selector(subviews), selectorToApply, selectorParam, predicateToApply);
}

// helper predicates, add additional ones as required

BOOL objectIsUITextField(id v) {
    return [v isKindOfClass:[UITextField class]];
}

BOOL objectIsUIScrollView(id v) {
    return [v isKindOfClass:[UIScrollView class]];
}

Here's a call to recursivelyApplySelectorToSubviews that invokes resignFirstResponder on each UITextField.

recursivelyApplySelectorToSubviews(topView, @selector(resignFirstResponder), nil, &objectIsUITextField);

So that's a quick example showing how Objective C's reflection can be applied to an everyday programming problem. Overall it doesn't strike me as too cumbersome; not great but not bad either. The drawbacks are it's pretty verbose, and the fact that selectors only work with object instances is annoying, as is the lack of a variable arguments list to performSelector. The worse part to my eye is the need to explicitly define each predicate. I wish Objective C had something like a lambda function.3

1 After writing this blog I came across UIView endEditing which performs the exact same function. Apparently it's been around since iOS 2.0; don't know how I missed it. sigh

2 Using an NSPredicate is clearly more idiomatic than a function pointer when writing Objective C but NSPredicate wasn't available on the iPhone when I first wrote this. It may be available now.

3Objective C now has blocks.

iPhoneCharlie