Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 422
gtaborga Avatar asked May 06 '11 16:05

gtaborga


People also ask

What is instance method in Objective-C?

An instance method operates on a particular instance of the class. For example, the accessors method that you implemented are all instance methods. You use them to set or get the instance variables of a particular object.

What does @dynamic do in Objective-C?

@dynamic just tells the compiler that the getter and setter methods are implemented not by the class itself but somewhere else (like the superclass or will be provided at runtime).

What is runtime in Objective-c?

The Objective-C runtime is a runtime library that provides support for the dynamic properties of the Objective-C language, and as such is linked to by all Objective-C apps. Objective-C runtime library support functions are implemented in the shared library found at /usr/lib/libobjc.


2 Answers

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.

like image 26
Justin Spahr-Summers Avatar answered Sep 18 '22 15:09

Justin Spahr-Summers


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?"
like image 108
Dave DeLong Avatar answered Sep 19 '22 15:09

Dave DeLong