8

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

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

[someInstance getSomething]

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

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

4

3 回答 3

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
}

这样做之后,onlyobject将收到被覆盖的方法,因为它将是唯一一个特殊类的实例。此外,此代码仅覆盖实例方法,但修改它以覆盖类方法并不难。

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

  1. 警告实施者:此代码是在浏览器中输入的
  2. 警告观察者:此代码不能很好地与键值观察配合使用
  3. 警告线程:这段代码看起来不是很线程安全
  4. 警告 ARC'erobjc_allocateClassPair()不能用 ARC 编译。
  5. 警告开发人员:乱搞一个对象的类是一件危险的事情。这种巫毒有完全合法的用途,但它们非常罕见。如果你认为你需要这样做,你可能是错的,应该在这里发布一个新问题:“这是我认为我需要做的;是吗?”
于 2011-11-26T16:49:17.593 回答
2

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

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

于 2011-11-26T11:57:00.760 回答
0

我不熟悉 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

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

于 2011-05-06T16:40:46.750 回答