Objective-C中的class_addMethod只能在特定实例上工作吗?

gta*_*rga 8 dynamic objective-c objective-c-runtime resolve

我正在尝试编写一些动态代码,用户可以尝试从类的特定实例调用方法,并在运行时解析它.检索信息的实现存在,但访问它的方法不存在,因为它基于每个实例.

例如,用户可能想要调用类中不存在的名为"getSomething"的方法:

[someInstance getSomething]
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我希望有一个实现已解析,它具有一个仅适用于正在处理的实例的变量返回类型.我正在考虑使用Objective-C中的class_addMethod,但我并不是100%肯定它的行为.在文档中,它声称这可以用于添加类或实例方法.调用此类是否仅将方法添加到特定实例或类中,以便之后创建的每个实例都将具有该方法?我还读到,一旦添加了一个方法,你就无法删除它.

也许我的方法不正确,所以如果知道任何替代方案我会很感激.我不能使用消息转发,因为没有类可以理解已经实现的选择器.

Dav*_*ong 8

另一种方法是使用动态子类:

- (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
}
Run Code Online (Sandbox Code Playgroud)

执行此操作后,只会object收到重写的方法,因为它将是唯一的特殊类的实例.此外,此代码仅覆盖实例方法,但修改它以覆盖类方法并不困难.

一如既往,通常的警告:

  1. 警告实施者:此代码在浏览器中输入
  2. 警告观察者:这个代码与键值观察不一致
  3. 警告Threader:此代码看起来不是非常线程安全的
  4. 警告ARC'er:objc_allocateClassPair()不能用ARC编译.
  5. 警告开发人员:用一个对象的类来捣乱是一件危险的事情.对于这种伏都教有完全合法的用途,但它们非常罕见.如果你认为你需要这样做,那你可能错了,并且应该在这里发一个新问题说:"这是我认为我需要做的;是吗?"


Nek*_*ios 0

我不熟悉 class_addMethod,但这也许可以帮助您澄清:

请记住,在 Objective-C 中,您并不是在“调用方法”,而是实际上在发送消息。因此,在任何实例化对象上执行 [anyObject anyMethodName] 是安全的。该对象可能会也可能不会响应该消息。

您可以使用 [anyObject respondsToSelector:@selector(@"anyMethodName")] 检查来检查对象是否愿意,如果是,则继续执行 [anyObject anyMethodName] 调用。我无法完全理解您的问题描述,但听起来您有一个充满对象的异构容器,这些对象可能会也可能不会响应调用。在 Objective-C 中,对容器中的每个对象进行“respondsToSelector:”检查是完全正常的事情,听起来像是很好的设计

如果每个对象返回某种不同类型的数据,您可以使用“id”泛型类型来处理。即 id returnData = [anyObject anyMethodName]; 然后,您可以对 returnData 使用内省,或者您可以根据“anyObject”的类进行不同的处理,由 [anyObject class] 检查;

所以就像,

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

我希望这有助于回答这个问题