gpt4 book ai didi

objective-c - 使用 objc_msgSend 调用实例方法

转载 作者:行者123 更新时间:2023-12-03 16:07:27 25 4
gpt4 key购买 nike

我正在尝试使用 objc_msgSend动态调用某个方法的方法。
假设我想从 A 类调用 B 类中的某个方法,而 B 类中有两个方法,例如:

- (void) instanceTestWithStr1:(NSString *)str1 str2:(NSString *)str1;
+ (void) methodTestWithStr1:(NSString *)str1 str2:(NSString *)str1;

我可以在A类中成功调用这样的类方法:
objc_msgSend(objc_getClass("ClassB"), sel_registerName("methodTestWithStr1:str2:"), @"111", @"222");

我也可以成功地在 A 类中调用这样的实例方法:
objc_msgSend([[objc_getClass("ClassB") alloc] init], sel_registerName("instanceTestWithStr1:str2:"), @"111", @"222");

但问题是要获得 B 类的实例,我必须调用“initWithXXXXX:XXXXXX:XXXXXX”而不是“init”,所以
将一些必要的参数传递给类 B 以执行初始化操作。所以我在类 A 中存储了一个 ClassB 的实例作为变量:
self.classBInstance = [[ClassB alloc] initWithXXXXX:XXXXXX:XXXXXX];

然后我像这样调用方法(成功):

问题是,我想通过简单地应用类名和方法 sel 来调用一个方法,比如“ClassName”和“SEL”,然后动态调用它:
  • 如果是类方法。然后称之为:
    objc_msgSend(objc_getClass("ClassName"), sel_registerName("SEL"));
  • 如果是实例方法,则在调用类中找到现有的类实例变量,然后:
    objc_msgSend([self.classInstance, sel_registerName("SEL"));

  • 所以我想知道是否有任何方法可以:
  • 检查一个类是否有给定的方法(我发现“responseToSelector”就是一个)
  • 检查类方法或实例方法中的给定方法(也许也可以使用 responseToSelector)
  • 检查一个类是否有给定类的实例变量所以我可以调用一个实例方法,如:
    objc_msgSend(objc_getClassInstance(self, "ClassB"), sel_registerName("SEL"));
  • 最佳答案

    您可能想要 read this .您实际上要问的是“我想创建一个新的调度程序”,要回答这个问题,您应该对现有调度程序的工作方式有一个透彻的了解。

    请告诉met,这是你在做什么?在语言之间架起一座桥梁?因为如果没有,你就会陷入一个非常有趣的兔子洞,探索该死的有趣,但可能不是一个非常有效或优雅的解决方案。

    现在:

    The problem is, I want to call a method by simply applying the classname and the method sel like "ClassName" and "SEL" and then call it dynamically:

    1. If it's a class method. then call it like: objc_msgSend(objc_getClass("ClassName"), sel_registerName("SEL"));

    Class klass = objc_getClass("ClassName"); // NSClassFromString(@"ClassName")
    SEL sel = sel_getUID("selector"); // NSSelectorFromString(@"selector");
    if ( [klass respondsToSelector:sel] )
    objc_msgSend(klass, sel);

    如果您有想要传递的参数,请参见下文。 NSInvocation理查德的回答是一种高级方法,但间接使用了 objc_msgSend() (并且 NSInvocation 有限制)。

    "2". If it's a instance method, find the existing class instance variable in the calling class then: objc_msgSend([self.classInstance, sel_registerName("SEL"));



    那没有意义。类没有实例变量。一个类的实例有一个实例变量,但是你可能需要一个特定的实例,而不是你在这个地方创建的一些随机实例。实例携带状态并随着时间的推移增加该状态。

    在任何情况下,您都可以轻松拨打 classInstance使用上述机制的类上的方法(这将完全没有意义——只需编写 [self classInstance] 并完成它),然后:
    id classInstance = [self classInstance];
    SEL sel = ... get yer SEL here ...;
    if ([classInstance respondsToSelector:sel])
    objc_msgSend(classInstance, sel);

    显然,如果您需要参数,请参见下文。

    So I want to know if there is any way to:

    1. Check if a class has a given method (I found "responseToSelector" will be the one)


    看上面。类响应 respondsToSeletor: .如果你想检查类的实例是否响应选择器,你可以调用 instancesRespondToSelector: .
    Class klass = ... get yer class on...;
    SEL someSelector = ... get that SEL ...;
    if ([klass instancesRespondToSelector:someSelector])
    objc_msgSend(instanceOfKlassObtainedFromSomewhere, someSelector);

    再次,争论?见下文。

    "2". Check if a given method in class method or instance method (maybe can use responseToSelector as well)



    看上面。给定一个类,您检查该类或实例是否响应任何给定的选择器。请注意,对于 NSObject 协议(protocol)中的许多选择器,类将响应许多 NSObject 实例方法,因为元类——类是其实例的类——实现了相当多的上述方法。

    "3". Check if a class has a instance variable of a given class So I can call a instance method like: objc_msgSend(objc_getClassInstance(self, "ClassB"), sel_registerName("SEL"));



    setter/getter 方法和实例变量之间的关系完全是巧合。不需要有一个 ivar,也不需要任何给定的 ivar 的 setter 和/或 getter。因此,这个问题没有意义,因为任意调用基于 ivar 名称的方法通常会失败。

    正如 Richard 建议的那样,您可以使用键值编码,但这意味着对传递给 setter 的值进行手动装箱,并对从非对象类型的 getter 检索到的值进行手动拆箱。

    在幕后,KVC 实现了一种启发式方法,以在类中搜索名称与请求名称最匹配的方法或 ivar。主要是因为它会做诸如搜索 _ 前缀之类的事情。 NSKeyValueCoding.h 头文件很有趣。

    在任何情况下,都不需要选择器。给定一个名称,只需执行以下操作:
    id foo = [myInstance valueForKey:@"iVarName"];

    和:

    [myInstance setValue:[NSNumber numberWithInt:42] forKey:@"ivarName"];

    显然,打字是一个主要问题。如果您有非对象类型,那么您将不得不处理将它们移入/移出 NSValue 容器的问题,并不是所有的东西都适合,这让您不得不对 KVC 方法/ivar 搜索算法进行逆向工程(不是太很难——只是一堆字符串操作和查找),然后传递如下任意参数。

    请注意,您对 objc_msgSend() 的两次调用在技​​术上是错误的,因为类型转换 objc_msgSend()到具有显式参数类型的非可变参数形式。你需要这样的东西:
    // - (void) instanceTestWithStr1:(NSString *)str1 str2:(NSString *)str1;
    void (*msgSendVoidStrStr(id, SEL, NSString*, NSString*) = (void*)objc_msgSend;
    msgSendVoidStrStr(...obj..., @selector(instanceTestWithStr1:str2:), str1, str2);

    这是因为可变参数 ABI 和显式参数类型 ABI 不一定在所有架构上都兼容。 ARC、IIRC 明确执行了这一点。

    还要注意,任意调用类或实例方法的概念,其中调用实例方法即时实例化类的实例,实际上没有多大意义。但是,嘿...你的代码。

    请注意,您也永远不想调用 sel_registerName()以这种方式;如果你要调用一个选择器,它最好已经存在。该函数显式存在,用于在运行时定义类。最好用 NSSelectorFromString()sel_getUid() (不幸的是,由于多年来没有纪律的程序员,实际上最终调用了 sel_registerName())。至少你的意图是正确的。

    现在,使用 objc_msgSend() as you want 要求您回答一个问题,而结果的答案将截然不同。一个答案是“哦,只做 X”的简单方法,另一个答案是“哦,天哪,你正朝着痛苦的道路前进”。

    问题: 您是否有一组固定的方法签名,还是必须传递一组任意的多种类型的参数?

    最终,有多少不同类型的参数将决定代码的复杂程度。如果您只有 0,1 或 2 个参数并且它们始终是对象,请坚持使用 invokeSelector: , invokeSelector:withObject:invokeSelector:withObject:withObject: .

    如果答案是“固定的方法签名集”,那么答案就在上面;只需声明一个函数指针,其中包含您想要使用的所有不同可能的方法签名,并在运行时选择正确的方法并将其作为上述函数调用调用。

    现在,如果答案是“具有许多不同参数组合的任意选择器集”,则答案要困难得多。您需要使用 libffi (或类似的东西)以编程方式执行编译器在编译时所做的工作 msgSendVoidStrStr(...obj..., @selector(instanceTestWithStr1:str2:), str1, str2); . libffi提供使用几乎任意参数和返回类型对调用进行编码所需的一切。

    使用起来并不容易。事实上,使用 libffi 构建自己的堆栈帧已经足够困难了,因此编写一个脚本来转储所有可能的调用组合并为每个组合创建一个覆盖函数可能会更容易,可能将参数作为 NSArray*。容器并在内部对其进行解码。类似(自动生成):
    void msgSendVoidStrStr(id obj, SEL _cmd, NSArray*args) {
    objc_msgSend(obj, _cmd, [args objectAtIndex:0], [args objectAtIndex:1]);
    }

    事实证明,这比编写一堆技巧性运行时代码更容易调试。

    关于objective-c - 使用 objc_msgSend 调用实例方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12488166/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com