如何在运行时确定协议方法是否是可选的?

6

我已经定义了我的协议。 我已经将我的协议方法中的两个标记为可选项。 在运行时,如何找出特定方法是否为可选项? 有没有办法找出来?


2
为什么在运行时需要这些信息?(只是好奇。) - Nikolai Ruhe
1
@NikolaiRuhe 没错,对你的评论点赞。总的来说,我们不应该做出假设,而是要测试功能。也就是说,我们应该测试 respondsToSelector: 而不是假设必需的方法已经实现。 - user529758
或者您可以自己制作一个字典,将所有方法的名称作为键,其值为YES/NO以检查是否需要。 - Anoop Vaidya
@H2CO3 是的,正确的问题是一个对象是否符合协议中特定可选方法。它是否是可选的是在编译时确定的。它是否被实现是在运行时确定的。 - Rob
2个回答

12
这应该符合你的要求:
BOOL MethodInProtocolIsRequired(Protocol *protocol, SEL methodSelector)
{
    struct objc_method_description methodDesc = protocol_getMethodDescription(protocol, methodSelector, YES, YES);
    return methodDesc.name != NULL;
}
请注意,我不评论使用此功能在运输代码中的可行性,尤其是因为您没有解释为什么要这样做。还要注意,当给出协议不包含方法的选择器时,该函数将返回NO。这基本上是合理的(毕竟,如果协议不包含方法,则不需要),但您可以通过检查协议是否将该方法作为可选方法包含并为所有三种情况(必需的、可选的、不在协议中)返回不同的内容来增加该函数的复杂性。

编辑:简单测试程序在此处:https://gist.github.com/4381753


我应该真正做这个,而不是我的过于复杂的方法。+1。(我可以在我的答案中加入这个吗?) - user529758
是的,我确保添加了归属 :) 这次应该是我需要阅读文档... -.- - user529758

3
我不知道答案,但只需要谷歌一分钟就可以解决。
你可以使用protocol_copyMethodDescriptionList()函数实现此功能,该函数是Objective-C运行时库(libobjc)的一部分。该函数的第二个参数是一个布尔标志,指示要复制到协议中的方法是否为必需。因此,如果该函数返回的列表中包含一个方法(使用适当的参数调用),则该方法是必需的。
SEL sctr = @selector(isThisMethod:requiredIn:theProtocol:);

struct objc_method_description *methods;
unsigned int nMethods;
methods = protocol_copyMethodDescriptionList(
    objc_getProtocol("MyProtocolName"), // or @protocol(MyProtocolName) if you don't need this kind of dynamism
    YES, // required?
    YES, // instance method? (in general, protocols declare instance methods)
    &nMethods
);

BOOL isRequired = NO;
int i;
SEL s;
const char *sctrStr = sel_getName(sctr);
for (i = 0; i < nMethods; i++) {
    s = methods[i].name;
    const char *sStr = sel_getName(s);
    if (strcmp(sctrScr, sStr) == 0) {
        isRequired = YES;
        break;
    }
}

free(methods);

if (isRequired) {
    // required
} else {
    // optional
}

所以,这是可能的,但有点过度设计了。正如我在你的问题评论中提到的那样,你不应该测试一个方法是否可选或必需,而应该测试一个实例是否响应特定的选择器。

编辑:是的,我应该阅读文档更深入一些,而不是复制整个宇宙。正如Andrew Madsen指出的那样,这可以简化为几行代码:

struct objc_method_description method;
method = protocol_getMethodDescription(
    objc_getProtocol("MyProtocolName"), // or @protocol(MyProtocolName)
    @selector(isThisSelector:required:)
    YES, // required?
    YES // instance method?
);

if (method.name != NULL) {
    // required
} else {
    // optional
}

1
如果你使用GNU C布尔值,你最终可以说你回答了一个Objective-C问题而没有任何ObjC!(尽管这非常技术性)。 - CodaFi
1
@CodaFi:或者C99的bool/_Bool - Peter Hosey
1
@CodaFi,@selector 怎么比 #define 更不像 Objective-C 呢? - Nikolai Ruhe
1
@H2CO3 我不认为修改一个已经能够工作(虽然不完美)的答案来重复另一个已有的答案是有意义的。 - Nikolai Ruhe
@NikolaiRuhe 你是想告诉我一个C类型(sel)和一个预处理宏是对我刚才说的话的好反驳吗?LULZ - CodaFi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接