3

I am in a situation where I want to dynamically generate getters and setters for a class at runtime (in a similar manner to what NSManagedObject does behind the scenes). From my understanding, this is possible using resolveInstanceMethod: on a specific class. At this point, you would have to use class_addMethod to dynamically add the method based on the selector. I understand this at a theoretical level, but I haven't delved much into the obj-c runtime, so I was curious if there were any great examples of how to do this. Most of my knowledge comes from this article:

http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtDynamicResolution.html

Any thoughts / examples?

dtuckernet
  • 7,817
  • 5
  • 39
  • 54
  • On the surface this sounds like a terrible idea. Why do you want to do it? What issues are you running into? – jtbandes Jul 17 '11 at 00:42
  • It's actually not a terrible idea :). As I mentioned, the purpose here is to function in a similar manner to NSManagedObject where subclassed objects can have their properties acted on in a manner determined by their superclass. For example, with Core Data the superclass defines the core login (including the dynamic getters and setters) to ensure that the data is stored in your persistent store. My approach is accomplishing something similar, but for an entirely different purpose than Core Data (I can't go into to many details here as it ties in with functionality currently under NDA). – dtuckernet Jul 17 '11 at 00:48
  • @jtbandes say that to Apple :) – Yuji Jul 17 '11 at 01:04

2 Answers2

9

The only nice discussion I know is at Mike Ash's blog post. It's not that hard, actually.

I once needed to split a big NSManagedObject subclass into two, but decided to keep the fact an implementation detail so that I don't have to rewrite other parts of my app. So, I needed to synthesize getter and setter which sends [self foo] to [self.data foo], automatically.

To achieve that, I did the following:

  1. Prepare the new method, already in my class.

    - (id)_getter_
    {
        return objc_msgSend(self.data, _cmd);
    }
    
    - (void)_setter_:(id)value 
    {
        objc_msgSend(self.data, _cmd,value);
    }
    

    Note that _cmd has the selector in it. So, usually, _cmd is either @selector(_getter_) or @selector(_setter_) in these methods, but I'm going to plug the implementation of _getter_ as the implementation of foo. Then, _cmd contains @selector(foo), and thus calls self.data's foo.

  2. Write a generic synthesizing method:

    +(void)synthesizeForwarder:(NSString*)getterName
    {
        NSString*setterName=[NSString stringWithFormat:@"set%@%@:",
              [[getterName substringToIndex:1] uppercaseString],[getterName substringFromIndex:1]];
        Method getter=class_getInstanceMethod(self, @selector(_getter_));
        class_addMethod(self, NSSelectorFromString(getterName), 
                        method_getImplementation(getter), method_getTypeEncoding(getter));
        Method setter=class_getInstanceMethod(self, @selector(_setter_:));
        class_addMethod(self, NSSelectorFromString(setterName), 
                        method_getImplementation(setter), method_getTypeEncoding(setter));
    }
    

    Note that this is a class method. So self stands for the class. Note also that I didn't hardcode type encodings (which tells Objective-C runtime what the arguments of the particular method are). The syntax of type encodings is documented, but constructing by hand is very error-prone; I wasted a few days that way until Mike Ash told me to stop it. Generate it using an existing method.

  3. Generate forwarders at the earliest possible time:

     +(void)load  
     {
         for(NSString*selectorName in [NSArray arrayWithObjects:@"foo", @"bar", @"baz",nil]){
            [self synthesizeForwarder:selectorName];
         }
     }
    

    This generates foo, setFoo:, bar, setBar:, and baz, setBaz:.

Hope this helps!

Yuji
  • 34,103
  • 3
  • 70
  • 88
5

Another example is one I wrote, called DynamicStorage, available here:

https://github.com/davedelong/Demos

The primary impetus behind it was this question, which was asking how to use an NSMutableDictionary as the backing store for any object ivar. I wrote a class that will generate getters and setters for any @property, respecting things like a custom getter/setter name, the object memory management policy, etc. The neat thing about it is that it's using imp_implementationWithBlock() so that it only has to calculate the appropriate property name once (and then captures and saves it as part of the block).

Community
  • 1
  • 1
Dave DeLong
  • 242,470
  • 58
  • 448
  • 498