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

I am trying to write dynamic code where a user can try to call a method from a specific instance of a class and enable it at runtime. There is an implementation for extracting information, but the method of accessing it is not because it is based on each instance.

For example, a user may need to call a method called "getSomething" that does not exist in the class:

[someInstance getSomething] 

In this situation, I want to have an allowed implementation that has a return type of variable that will only apply to the instance being processed. I was considering using class_addMethod from Objective-C, but I'm not 100% sure of its behavior. In the documentation, he claims that this can be used to add class or instance methods. Does this class allow you to add this method only to a specific instance or class so that each instance created subsequently has a method on it? I also read that after adding a method, you cannot delete it.

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

+7
source share
3 answers

Another way you could do this is to create 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 that, only object will receive an overridden method, because it will be the only thing that is an instance of a special class. Furthermore, this code only overrides instance methods, but it would be difficult to change to override class methods.

As always, the usual warnings:

  • Caveat Implementor: This code was entered in the browser.
  • Caveat Observer: This code does not render well when observing key values.
  • Caveat Threader: This code does not look very thread safe
  • Caveat ARC'er: objc_allocateClassPair() cannot be compiled using ARC.
  • Caveat Developer: Offsetting with an object class is a dangerous thing. There is a completely legal application for this kind of voodoo, but they are very rare. If you think you need to do this, you are probably mistaken, and a new question should be asked here: "This is what I think I need to do, right?"
+7
source

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

Instead, if you really need this behavior, you can implement - forwardInvocation: where the receiving object can decide if it has enough information to execute the message. Note that to implement -forwardInvocation: it is usually required to implement - methodSignatureForSelector: and - Also responds: Selector:.

+2
source

I am not familiar with class_addMethod, but maybe this can help you clarify:

Remember that in Objective-C you are not calling a method, but you are really sending a message. So it's safe to do: [anyObject anyMethodName] on any instance of the object. This object may or may not reply to a message.

You can check whether or not the object will be using [anyObject responds to SoSelector: @selector (@ "anyMethodName")], and if it is YES, then continue and call [anyObject anyMethodName]. I cannot fully understand your description of the problem, but it looks like you have a heterogeneous container full of objects that may or may not answer the call. Doing this "responsesToSelector:" checking every object in a container is an absolutely normal thing in Objective-C and sounds like a good design

If each object returns some other data type, you can process it using the id type. That is, id returnData = [anyObject anyMethodName]; Then you can use introspection in returnData or you can handle things differently depending on what class "anyObject" is checked by [class anyObject];

So that,

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

Hope this helps answer the question.

0
source

All Articles