目标C:块与选择器与协议

时间:2010-08-03 01:38:45

标签: objective-c

我经常发现自己编写的“实用程序”类可以在整个项目中重复使用。

例如,假设我有一个“地址簿”视图。我可能想使用我的地址簿来选择向谁发送电子邮件,或者可能是谁被添加到会议请求中。

我开发了这个视图控制器,因此它可以被电子邮件控制器和会议控制器使用,具有某种回调机制,让调用者知道用户已完成从地址簿中选择某人,或者他们是取消。

在这种情况下,似乎基本上有四种(合理的)方法;

  • 在AddressBookController上创建“AddressBookDelegate”协议和相应的委托属性。然后使用协议中定义的消息来传达结果(类似于UIActionSheetDelegate)。

  • 在AddressBookController上创建一个“非正式”“AddressBookDelegate”协议和相应的委托属性,但委托属性的类型将为“id”,并将在运行时使用“respondsToSelector:”进行检查以查看是否委托实现了我们需要的方法(似乎大多数框架的东西都是以这种方式开始的)。

  • 将AddressBookController传递给代表委托的id,以及两个SEL,用于指定用户选择用户或取消请求时要调用的方法。我看到的好处是;假设一个控制器支持同时发送电子邮件和设置会议(我知道在这个例子中看起来好像设计不好......但可以想象一个更通用的情况,这对于实用程序类来说似乎是完全合理的) - 在这种情况下你可以传递AddressBookController不同的SEL,具体取决于您是将用户添加到电子邮件中,还是将用户添加到会议中...相对于iVar的巨大改进,以指示控制器的“状态”。

  • 传递AddressBookController两个块;一个在用户从地址簿中选择某人时运行,另一个在用户取消请求时运行。

块对我来说非常有用,而且更加优雅,我发现自己几乎不知道何时不使用它们。

我希望StackOverflow社区中更有经验的成员能够帮助他们解决这个问题。

2 个答案:

答案 0 :(得分:27)

执行此操作的“传统”方法是使用协议。在将@protocol添加到语言之前使用了非正式的,但这是在我的时间之前,并且至少在过去的几年里,不鼓励使用非正式协议,特别是考虑到@optional说明​​符。至于通过两个SEL的'委托',这似乎比宣布一个正式的协议更难看,而且对我来说似乎并不合适。块是非常新的(特别是在iOS上),随着这些事情发生,虽然我们还没有看到大量的文档/博客上最好的尝试和真实的风格,我喜欢这个想法,这似乎是一个事物块最适合:整洁的新控制流结构。

基本上我想说的是这些方法中的每一种都有不同的年龄,没有一种比上一种更好,除了风格,这显然是非常重要的,并且最终为什么这些东西都被创造了。基本上,请选择您认为最新的东西,它应该是块或正式协议,并且您的混淆最有可能来自阅读冲突的来源,因为它们是在不同时间编写的,但是从时间上看,它是清楚地看到哪些取代了其他人。

[Controller askForSelection:^(id selection){
  //blah blah blah
} canceled:^{
  //blah blah blah
}];

可能比定义两个额外的方法,一个协议(正式或其他)或传递SEL并将它们存储在ivars等中更简洁。

答案 1 :(得分:17)

我会选择你的第一种方法。它在Cocoa中是一个久经考验的模式,似乎非常适合你正在做的事情。

关于其他方法的一些评论:

  1. 非正式协议 - 我没有看到在正式协议上做这件事有什么好处。自从正式协议获得@optional方法以来,非正式协议的效用要小得多。
  2. 传递SEL - 我不认为这是Cocoa中已建立的模式。我个人不会认为它比代表方法更好,但如果它更适合你的想法,那就去吧。你并没有真正摆脱国家;你只是在转变成别的东西。就个人而言,我更喜欢有一个我可以设置和检查的ivar,而不必使用选择器类型。
  3. 传递积木 - 这是一种新时代的方法,它有一些优点。我认为你需要小心,因为在我看来,它并不能很好地扩展。例如,如果NSTableView的委托和数据源方法都是块,我个人会觉得有点烦人。想象一下,如果你想设置10个不同的块,你的-awakeFromNib(或其他)方法会非常大。在这种情况下,个别方法似乎更合适。但是,如果您确定自己永远不会超越两种方法,那么块方法似乎更合理。