【问题标题】:Can class_addMethod in Objective-C work only on a specific instance?Objective-C 中的 class_addMethod 只能在特定实例上工作吗?
【发布时间】:2011-05-06 16:25:28
【问题描述】:

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

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

[someInstance getSomething]

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

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

【问题讨论】:

    标签: objective-c dynamic objective-c-runtime resolve


    【解决方案1】:

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

    - (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
    }
    

    完成此操作后,只有object 会收到被覆盖的方法,因为它将是唯一一个特殊类的实例。此外,这段代码只覆盖了实例方法,但修改它以覆盖类方法并不难。

    和往常一样,通常的警告:

    1. Caveat Implementor:此代码是在浏览器中输入的
    2. 注意观察者:这段代码不能很好地与键值观察配合使用
    3. Caveat Threader:这段代码看起来不是很线程安全
    4. 警告 ARC'erobjc_allocateClassPair() 不能用 ARC 编译。
    5. 开发者注意事项:乱搞一个对象的类是一件危险的事情。这种伏都教有完全合法的用途,但它们非常很少见。如果您认为自己需要这样做,那么您可能错了,应该在此处发布一个新问题:“这是我认为我需要做的事情;是吗?”

    【讨论】:

    • 这里还涉及到时间/空间的权衡。如果每个实例可能支持不同的方法,则运行时中动态类的数量可能会变得难以处理。相反,如果确实只有一种方法,有些方法有,有些方法没有,方法转发可能会产生不可接受的速度成本,而动态子类可能会快得多。
    【解决方案2】:

    class_addMethod() 将实例方法添加到类对象或将类方法添加到元类对象。换句话说,你永远不能只为一个类的一个实例添加一个方法。

    相反,如果你真的需要这种行为,你可以实现-forwardInvocation:,接收对象可以决定它是否有足够的信息来完成消息。请注意,-forwardInvocation: 的实现通常还需要实现 -methodSignatureForSelector:-respondsToSelector:

    【讨论】:

      【解决方案3】:

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

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

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

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

      喜欢,

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

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

      【讨论】:

      • 感谢您抽出宝贵时间回答我的问题。这实际上并不是我想要的,但它确实让我对我不清楚的其他领域有所了解。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-10
      • 2020-05-10
      • 1970-01-01
      • 2013-03-01
      • 2012-07-08
      相关资源
      最近更新 更多