如果我不得不在对象上调用“respondsToSelector:”,那么将一个方法定义为可选实际上对我有什么用呢?
例如,假设我有一些像这样的代码
id<MyProtocol> myObj = [[MyClass alloc] init];
if([myObj respondsToSelector:@selector(aMethod)]){
[myObject aMethod];
}
只要“MyClass”实现“aMethod”,这个代码不会运行完全相同或不运行MyProtocol定义“aMethod”吗?
我可以看到纯粹从代码可读性的角度定义这个可选协议的用法,但是从技术的角度来看它是否真的有任何影响(除了不必在头文件中声明方法)。
答案 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_msgSend
或objc_msgSend_stret
或objc_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)