views:

229

answers:

2

Is there a way to do something similar to KVC but use the message name as the key itself? For example, rather than valueForKey:, is there a way for an object to respond to all messages?

For example, say you have an XML document:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ...>
<employees>
  <employee>
    <name>Bill</name>
    <department>Accounting</department>
  </employee>
  <employee>
    <name>John</name>
    <department>Human Resources</department>
  </employee>
</employees>

Besides using the already-available NSXMLDocument and co., would there be a way to implement some sort of abstraction so you could do:

MyXML *xmlDoc = [[MyXML alloc] initWithContentsOfFile:@"test.xml"];

NSLog (@"%@", [[[xmlDoc employees] first] department]);

[[[xmlDoc employees] first] setDepartment:@"Management"];

I chose XML just as an example, but I want to know whether this is possible at all, and whether or not it involves getting too close to the Objective-C runtime or whether it is supported by the runtime itself, and how I would go about implementing something like this. This is purely for experimental purposes and I understand that there will likely be significant performance costs.

EDIT:
If possible, I'd like to avoid existing frameworks such as Cocoa and use the base Objective-C object Object.

+3  A: 

You need to implement forwardInvocation: and methodSignatureForSelector: to handle unrecognized messages. It is described in the NSObject reference.

EDIT: Wikipedia has an example of how forwarding can be accomplished for Object, basically by implementing forward: and performv:.

codelogic
Thanks for that helpful info, but that's specific to Cocoa, I'm looking for an overall Objective-C use. For example, compiling just straight Objective-C in Linux or Windows or Mac.
dreamlax
+1  A: 

Thanks to codelogic, I've found out all you need to do for GCC's Objective-C runtime is:

#import <obc/Object.h>
#import <stdio.h>

@interface RespondsToEverything : Object {}
@end

@implementation RespondsToEverything
- (retval_t) forward:(SEL) sel :(arglist_t) argFrame
{
    fprintf (stderr, "Received: %s\n", get_sel_name (sel));
}
@end

int main (int argc, char *argv[])
{
    id test = [RespondsToEverything new];
    [test asdfasdfasdfasdf];
    [test zxcvzxcvzxcvzxcv];

    return 0;
}

The output is:

Received: asdfasdfasdfasdf
Received: zxcvzxcvzxcvzxcv

But of course, GCC complains that it cannot find method signatures for asdfasdfasdfasdf and zxcvzxcvzxcvzxcv. Not a big deal, it still shows that the concept is doable. In Apple's runtime you can use sel_getName(sel) instead of get_sel_name(sel).

dreamlax