我想指定一个带有可选例程的Objective-C协议。当例程没有由符合协议的类实现时,我想在其位置使用默认实现。协议本身是否存在我可以定义此默认实现的位置?如果没有,那么减少复制和粘贴这个默认实现的最佳做法是什么?
答案 0 :(得分:17)
Objective-C协议没有默认实现的可供性。它们纯粹是方法声明的集合,可以由其他类实现。 Objective-C中的标准做法是在运行时测试一个对象,看它是否在调用该方法之前响应给定的选择器,使用 - [NSObject respondsToSelector:]。如果e对象没有响应给定的选择器,则不调用该方法。
您可以实现结果的一种方法是定义一个封装您在调用类中寻找的默认行为的方法,并在对象未通过测试时调用该方法。 / p>
另一种方法是在协议中使用该方法,并在您可能不想提供特定实现的任何类的超类中提供默认实现。
也可能有其他选项,但一般来说,在Objective-C中没有特定的标准实践,除非我不是第一次调用给定的方法,如果它没有被对象实现,上面的段落。
答案 1 :(得分:17)
答案 2 :(得分:4)
答案 3 :(得分:1)
正如Ryan所提到的,没有协议的默认实现,在超类中实现的另一个选择是实现一个“Handler”类,它可以包含在任何想要提供默认实现的类中,相应的然后,方法调用默认的处理程序实现。
答案 4 :(得分:1)
我最终创建了一个具有该方法的默认实现的宏。
我已经在协议的头文件中定义了它,然后它在每个实现中只是一个单行。
这样,我不必在几个地方更改实现,并且它在编译时完成,因此不需要运行时魔术。
答案 5 :(得分:1)
我同意“w.m.”一个非常好的解决方案是将所有默认实现放入一个接口(与协议同名)。在任何子类的“+ initialize”方法中,它可以简单地将任何未实现的方法从默认接口复制到自身。
以下帮助函数对我有用
#import <objc/runtime.h>
// Get the type string of a method, such as "v@:".
// Caller must allocate sufficent space. Result is null terminated.
void getMethodTypes(Method method, char*result, int maxResultLen)
{
method_getReturnType(method, result, maxResultLen - 1);
int na = method_getNumberOfArguments(method);
for (int i = 0; i < na; ++i)
{
unsigned long x = strlen(result);
method_getArgumentType(method, i, result + x, maxResultLen - 1 - x);
}
}
// This copies all the instance methods from one class to another
// that are not already defined in the destination class.
void copyMissingMethods(Class fromClass, Class toClass)
{
// This gets the INSTANCE methods only
unsigned int numMethods;
Method* methodList = class_copyMethodList(fromClass, &numMethods);
for (int i = 0; i < numMethods; ++i)
{
Method method = methodList[i];
SEL selector = method_getName(method);
char methodTypes[50];
getMethodTypes(method, methodTypes, sizeof methodTypes);
if (![toClass respondsToSelector:selector])
{
IMP methodImplementation = class_getMethodImplementation(fromClass, selector);
class_addMethod(toClass, selector, methodImplementation, methodTypes);
}
}
free(methodList);
}
然后在类初始化程序中调用它,例如......
@interface Foobar : NSObject<MyProtocol>
@end
@implementation Foobar
+(void)initialize
{
// Copy methods from the default
copyMissingMethods([MyProtocol class], self);
}
@end
Xcode将为您提供有关Foobar缺失方法的警告,但您可以忽略它们。
此技术仅复制方法,而不是ivars。如果方法正在访问不存在的数据成员,则可能会出现奇怪的错误。您必须确保数据与代码兼容。就好像你从Foobar做了一个reinterpret_cast到MyProtocol。