Dynamic Getters and Setters with Objective C

前端 未结 2 1234
天命终不由人
天命终不由人 2021-01-01 06:43

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). Fr

相关标签:
2条回答
  • 2021-01-01 07:16

    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!

    0 讨论(0)
  • 2021-01-01 07:36

    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).

    0 讨论(0)
提交回复
热议问题