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

发布于 2024-11-06 04:33:56 字数 596 浏览 1 评论 0原文

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

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

[someInstance getSomething]

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

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

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

假装不在乎 2024-11-13 04:33:56

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

- (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. Caveat Observer:此代码不能很好地进行键值观察
  3. Caveat Threader :此代码看起来不太线程安全
  4. 请注意 ARC'erobjc_allocateClassPair() 无法使用 ARC 进行编译。
  5. 警告开发人员:乱搞对象的类是一件危险的事情。这种巫术有完全合法的用途,但非常罕见。如果您认为需要这样做,那么您可能错了,应该在这里发布一个新问题说:“这就是我认为我需要做的;是吗?”

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?"
绳情 2024-11-13 04:33:56

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

相反,如果您确实需要这种行为,您可以实现 -forwardInitation:,其中接收对象可以决定是否有足够的信息来履行消息。请注意,-forwardInitation:的实现通常需要实现-methodSignatureForSelector:-respondsToSelector: 也是如此。

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.

就此别过 2024-11-13 04:33:56

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

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

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

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

就像,

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

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

I'm not familiar with class_addMethod, but maybe this can help clarify for you:

Remember that in Objective-C you aren't "calling a method" but you're actually sending a message. So it's safe to do: [anyObject anyMethodName] on any instantiated object. This object may or may not respond to the message.

You can check whether an object will or not by using [anyObject respondsToSelector:@selector(@"anyMethodName")] check, and if that's YES, then go ahead and do [anyObject anyMethodName] call. I can't fully understand your problem description but it sounds like you have a heterogeneous container full of objects that may or may not respond to the call. Doing this "respondsToSelector:" check on each object in the container is a totally normal thing in Objective-C and sounds like good design

If every object returns some different type of data, you can handle that using the 'id' generic type. That is, id returnData = [anyObject anyMethodName]; Then, you can either use introspection on the returnData, or you could handle things differently based on what the class of 'anyObject' is, checked by [anyObject class];

So like,

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

I hope this helps answer the question

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文