Objective-C: One Hack to Log Methods

Jim Correia recently tweeted a query asking if it were possible to log all attempts to message through nil in Objective-C.

It is, but not through public API. But that isn’t the subject of this particular post (it’ll be the next post).

In the process of writing up a general solution to Jim’s query, it reminded me of a new feature in Objective-C 2.0 that can be a tremendously handy debugging and exploration hack.

Classes in Objective-C 2.0 can dynamically resolve methods by implementing one or both of the following methods:

+ (BOOL)resolveClassMethod:(SEL)sel;
+ (BOOL)resolveInstanceMethod:(SEL)sel;

The intended purpose of said methods is to allow code to provide an implemention of a method on demand. If the resolver methods can provide an implementation, it uses the function…

BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types);

… to add an implementation of the requested method to the class. If not, they return NO and the normal forward-or-fail mechanisms kick in.

You can easily create a proxy class that will log a method whenever it is invoked. The simplest form is as follows:

@interface LogEmAsTheyAreInvoked
@end

@implementation LogEmAsTheyAreInvoked
+ (BOOL)resolveInstanceMethod:(SEL)name
{
    NSLog(@"Instance resolving %@", NSStringFromSelector(name));
    return NO;
}

+ (BOOL)resolveClassMethod:(SEL)name
{
    NSLog(@"Class resolving %@", NSStringFromSelector(name));
    return NO;
}
@end

Of course, this is pretty much useless. Without an implementation of…

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;

… the runtime will rapidly decide that all viability is defunct, thus aborting your process. Even the most basic of services, such as allocation, cannot be handled directly. If you need to allocate an instance of the above, use something like:

class_createInstance(objc_getClass("LogEmAsTheyAreInvoked"), 0)

If you want to be particularly tricky, you could supply the proxied class’s instance size instead of the zero and then plug in the method implementations from the original class into the proxied class as each method is invoked. Behavior that tests the class hierarchy may be surprising.

On the slightly less tricky front, you could also create functionality like NSProxy and have the instance of LogEmAsTheyAreInvoked wrap — proxy — some instance of some class. Then it is just a matter of forwarding the method invocations, as desired (which could also directly be used for logging but lacks the nice & automatic “only invoked once” nature of the above methods).

In any case, a neat hack. One of many possible hacks in this vein and not the most general purpose of ’em. Potentially useful for debugging. Your mileage may vary.



2 Responses to “Objective-C: One Hack to Log Methods”

  1. bbum’s weblog-o-mat » Blog Archive » Objective-C: Logging Messages to Nil says:

    […] This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 2.5 License. Amazon.com Widgets « Objective-C: One Hack to Log Methods […]

  2. Log all accessed methods in Objective-C with DTrace says:

    […] would take a lot of NSLog’s. I started looking around and was pointed to bbums blog about doing something similar. However it still would take adding some code, plus I couldn’t get it working right with the […]

Leave a Reply

Line and paragraph breaks automatic.
XHTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>