开发者

Can class_addMethod in Objective-C work only on a specific instance?

I am trying to write some dynamic code where a user can try calling a method from a specific instance of a class and have it 开发者_开发知识库be resolved at runtime. The implementation to retrieve the information exists but the method to access it does not because it is on a per instance basis.

For example, a user may want to call a method named "getSomething" which doesn't exist in the class:

[someInstance getSomething]

In this situation, I want to have an implementation resolved which has a variable return type that will only apply to the instance being worked on. I was considering using class_addMethod from the Objective-C but I am not 100% sure of its behavior. On the documentation it claims that this can be used to add class or instance methods. Does calling this class add the method to only the specific instance or to the class so that every instance created afterward will have the method on it? I also read that once a method is added you can't remove it.

Perhaps my approach isn't correct so if any alternatives are known I would appreciate it. I cannot use message forwarding because there is no class that understands the selector already implemented.


Another way you could do this is with a dynamic subclass:

- (void)addCustomMethodToObject:(id)object {
  Class objectClass = object_getClass(object);
  SEL selectorToOverride = ...; // this is the method name you want to override

  NSString *newClassName = [NSString stringWithFormat:@"Custom_%@", NSStringFromClass(objectClass)];
  Class c = NSClassFromString(newClassName);
  if (c == nil) {
    // this class doesn't exist; create it
    // allocate a new class
    c = objc_allocateClassPair(objectClass, [newClassName UTF8String], 0);
    // get the info on the method we're going to override
    Method m = class_getInstanceMethod(objectClass, selectorToOverride);
    // add the method to the new class
    class_addMethod(c, selectorToOverride, (IMP)myCustomFunction, method_getTypeEncoding(m));
    // register the new class with the runtime
    objc_registerClassPair(c);
  }
  // change the class of the object
  object_setClass(object, c);
}

id myCustomFunction(id self, SEL _cmd, [other params...]) {
  // this is the body of the instance-specific method
  // you may call super to invoke the original implementation
}

After doing this, only object will have received the overridden method, because it will be the only thing that's an instance of the special class. Also, this code only overrides instance methods, but it wouldn't be hard to modify it to override class methods.

As always, the usual warnings:

  1. Caveat Implementor: this code was typed in a browser
  2. Caveat Observer: this code does not play well with key-value observing
  3. Caveat Threader: this code doesn't look very thread-safe
  4. Caveat ARC'er: objc_allocateClassPair() cannot be compiled with ARC.
  5. Caveat Developer: mucking around with an object's class is a dangerous thing. There are perfectly legitimate uses for this sort of voodoo, but they are very rare. If you think you need to do this, you're probably wrong, and should post a new question here saying: "this is what I think I need to do; do I?"


class_addMethod() adds an instance method to a class object or a class method to a metaclass object. In other words, you can never add a method just to one instance of a class.

Instead, if you really need this behavior, you could implement -forwardInvocation:, where the receiving object can decide if it has enough information to fulfill the message. Note that an implementation of -forwardInvocation: typically requires implementing -methodSignatureForSelector: and -respondsToSelector: as well.


I'm not familiar with class_addMethod, but maybe this can help clarify for you:

Remember that in Objective-C you aren't "calling a method" but you're actually sending a message. So it's safe to do: [anyObject anyMethodName] on any instantiated object. This object may or may not respond to the message.

You can check whether an object will or not by using [anyObject respondsToSelector:@selector(@"anyMethodName")] check, and if that's YES, then go ahead and do [anyObject anyMethodName] call. I can't fully understand your problem description but it sounds like you have a heterogeneous container full of objects that may or may not respond to the call. Doing this "respondsToSelector:" check on each object in the container is a totally normal thing in Objective-C and sounds like good design

If every object returns some different type of data, you can handle that using the 'id' generic type. That is, id returnData = [anyObject anyMethodName]; Then, you can either use introspection on the returnData, or you could handle things differently based on what the class of 'anyObject' is, checked by [anyObject class];

So like,

if([anyObject class] == MyGreatClass) // recast data to MyGreatClassCoolReturnType

I hope this helps answer the question

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜