协议中可选方法的重点是什么?

时间:2011-01-29 20:37:12

标签: objective-c protocols

如果我不得不在对象上调用“respondsToSelector:”,那么将一个方法定义为可选实际上对我有什么用呢?

例如,假设我有一些像这样的代码

id<MyProtocol> myObj = [[MyClass alloc] init];
if([myObj respondsToSelector:@selector(aMethod)]){
  [myObject aMethod];
}

只要“MyClass”实现“aMethod”,这个代码不会运行完全相同或不运行MyProtocol定义“aMethod”吗?

我可以看到纯粹从代码可读性的角度定义这个可选协议的用法,但是从技术的角度来看它是否真的有任何影响(除了不必在头文件中声明方法)。

7 个答案:

答案 0 :(得分:8)

它在锡上的说法非常相似:它与可选功能有关。如果您的aMethod包含必要功能以使您的应用程序正常运行,那么它应该是@required。否则,如果它为实现者提供了执行其他操作的附加功能,但是它的缺失不会对它应该工作的方式产生负面影响(即,实现者没有响应@selector(aMethod)选择器),你可以把它@optional

您在视图委托协议中看到了很多 。以iOS UITableViewDelegate为例:这是一组委托协议的方法,用于定义表视图的页眉和页脚的视图:

  • tableView:viewForHeaderInSection:
  • tableView:viewForFooterInSection:
  • tableView:heightForHeaderInSection:
  • tableView:heightForFooterInSection:

如果委托没有实现这些,UIKit只会绘制给定tableView的默认部分页眉和页脚,它们预先打包了默认的UITableView元素。但是,如果实现它们,UIKit将使用tableView这些方法提供的自定义节头和页脚视图。

@optional关键字几乎告诉 person 编写一个类来实现这些方法是可选的委托。我相信UIKit无论如何都会在内部进行conformsToProtocol:respondsToSelector:检查。

答案 1 :(得分:1)

@optional / @required不只是为了人们 - 尽管这本身就是一个很好的理由! - 它也适用于机器。编译器,静态分析器等可以使用它们来确定实现@protocol的类是否提供了所有必需的方法,并确定提供了哪些可选方法。

答案 2 :(得分:0)

另请参阅Apple的Communicating with Objects,其中讨论了委托,协议和选择器。虽然它在Mac OS X下列出,但大多数(如果不是全部)似乎也适用于iOS。

答案 3 :(得分:0)

还有另外一点 - 在您发送具有特定名称的消息之前,必须将具有该名称的方法声明为某处对于使用它的范围可见。 (它不必被声明为您发送消息的特定类型的方法 - 这是一个单独的问题,动态类型和静态类型检查。但必须声明某处作为某些类或协议的方法,即使您根本不使用该类或协议。)这是因为编译器必须翻译一个消息调用objc_msgSendobjc_msgSend_stretobjc_msgSend_fpret,具体取决于方法的返回类型;所以编译器必须知道方法的签名。

因此,一个可选的协议方法用于声明该方法;如果你没有在协议中包含该方法声明,那么调用者可能无法调用该方法,因为没有声明它。

答案 4 :(得分:0)

协议仅用于文档和编译时检查。程序运行后,运行时不知道或关心对象具有哪些静态类型(除了newacct提到的方法返回结构的情况)。

你可以完全没有协议,除了那时没有编译时检查特定对象在传递给特定API时是否具有所需的方法。如果您愿意,可以将所有对象声明为类型id,但这会有效地关闭编译器,检查发送给它们的任何消息是否由对象实现。它还会阻止您对属性使用点表示法。

一旦你声明一个对象符合一个协议,你就会得到协议,例如

id<MyProtocol> foo;

立即将方法重新打开。没有可选方法,这意味着

if ([foo respondsToSelector: @selector(myOptionalSelector)])
{
    [foo myOptionalSelector];
}

会标记编译器警告。使用@optional方法可以抑制警告并阻止编译器猜测返回和参数类型。

答案 5 :(得分:0)

我认为最大的不同就是调用方法的方法!

如果您没有定义协议,则需要使用方法performSelector:来调用未知方法。它可以工作,但在这种情况下,我们受限于您的方法的参数的数量和类型。实际上,您不能提供非对象参数,并且您可以使用performSelector:withObject:withObject:传递不超过2个参数。如果您想要更多参数,则需要使用数组或字典。

id<MyProtocol> myObj = [[MyClass alloc] init];
if([myObj respondsToSelector:@selector(aMethod:)]){
  NSArray *args = [NSArray arrayWithObjects:@"First arg",[NSNumber numberWithInt:2],[NSNumber numberWithBool:YES],nil];
  [myObject performSelector:(@selector(aMethod:) withObject:args];
}

否则,如果使用可选方法定义协议,则只需正常调用它:

id<MyProtocol> myObj = [[MyClass alloc] init];
if([myObj respondsToSelector:@selector(aMethodWithFirstArg:second:third:)]){
  [myObject aMethodWithFirstArg:@"First arg" second:2 third:YES];
}

同样的结果,但我更喜欢第二个与协议!

答案 6 :(得分:-1)

Morgancodes,

如果使用协议进行类型检查,则不必为协议中声明的每个方法调用respondsToSelector。可以找到一个简单的解释Here