"Toll Free Bridging" for your own data

In the course of working on a quick side project, I came across a few interesting items of note for those working on Objective-C projects.

I was looking for a way to recreate the behavior shown by some Core Foundation objects in that they may be cost-free casted to their equivalent type in the Cocoa framework. An example:

CFStringRef my_string = CFSTR("a string");
NSString* also_my_string = (NSString*) my_string;

It turns out that, should you have a need to represent a struct also as a subclass of some NSObject, you can do so by having your struct include a member of type Class (or struct objc_class*) named isa. The isa member is used by Objective C to access the table of methods and class hierarchy that your object (or struct, but we're splitting hairs) is associated with. A much more elaborate discussion regarding the isa member can be found at Mike Ash's website, which is damn near invaluable for learning more about Objective C.

Once you'e added the isa member to your struct, all that remains is to reflect your struct's internal structure in the NSObject representation you're creating. While it may seem obvious to some, it should bear worth noting that the order of declaration in your NSObject subclass should mimic the order of declaration in your struct. Here's an example:

typedef struct {
  Class isa;
  int   some_number;
  BOOL  some_flag;
} MyDumbStruct;

@interface MyDumbObject : NSObject {
  int some_number;
  BOOL some_flag;
}
-(int) someMethod;
@end

Now that you've done this, you'll be able to cast a properly created MyDumbStruct to a MyDumbObject instance. I say properly created because you will need to populate the isa member of your struct with the pointer to the Class object that correctly represents the class your struct will be associated with. You can do this by accessing the class method of the, ur, class in question. For example:

MyDumbStruct s;
s.isa = [MyDumbObject class];

That's it - and here's what you end up with in terms of capability:

MyDumbStruct s = { .isa = [MyDumbObject class],
                   .some_number = 2,
                   .some_flag = TRUE };
MyDumbObject *o = &s;
NSLog("My dumb object has a number: %i", [o someMethod]);

In my next post, I'll show what I've been working on that actually uses this technique.

Addendum: This link elaborates on what I described above; it also describes usage of the @defs keyword, which will allow you to only have to declare the member variables for your Object once, and have them inserted in your struct at compile time.

comments powered by Disqus