在类实例上使用委托的充分理由?

时间:2012-08-15 10:04:52

标签: objective-c delegates protocols

我被问到一个关于代表的问题,我对正确答案有些怀疑:

当您创建您自己的协议时,使用委托或仅仅实例化该类并调用简单方法之间的区别是什么?

我想:与代表一起,你不需要实例化一个新的全班,你可以在课堂外设置代表,只需用" delegate"来调用方法。使用代表避免无限制导入也是一件好事吗?当classA导入classB时,如果你实例化该类而不是使用委托,classB也会导入classA,它会导致崩溃吗?

创建自己的协议/代理时还有其他充分的理由吗?

感谢分享

3 个答案:

答案 0 :(得分:1)

好吧,看一个例子。

假设您有“某事”恰好将窗口显示为其正在执行的操作的一部分。没有委托或类似的设置,你必须是“Window”的子类才能处理窗口的东西,你必须成为其他东西的子类来处理你正在做的其他事情。所以它要么不可能,要么你会遇到各种各样的遗传怪异。

因此,您最终可能会创建一个子类,以及“Window”的子级,并为两者创建实例对象,并让它们以某种方式进行通信。这正是委托所做的,除了你必须一遍又一遍地做各种各样的事情。就像想象一个有10个按钮的窗口,每个窗口都需要一个按钮sublcass,除了在你的实际类上调用“ButtonXClicked”之外,它并没有那么多。再次,看到这里重新实现的“委托”(是的,在Cocoa中它不是委托;那将是目标/行动。但它与代表并没有什么不同)?

因此,代理几乎可以方便地连接那些实际上没有“派生类”所暗示的关系的对象。

它还允许您连接“在别处创建”的对象,比如当您拥有创建对象的东西并将其返回给您时:您不能真正替换那里的子类,但API可能仍然允许您做一个“setDelegate:” - 就像把对象连接到你的应用程序一样。

有时代理人更合适,有时候子类更好,而且可能会出现无关紧要的情况。

答案 1 :(得分:1)

我认为委托的主要优点是它们允许您在运行时更改对象的行为。它们还有助于强化功能区域之间的分离。

让我们看一个具体的例子:NSTableView的数据源。 NSTableView为OS X中的表提供UI实现。数据源委托提供了获取数据以填充表的功能。

在我来到Objective-C并使用C ++和Java编程之前的过去,我会通过让UI类使用一些受保护的方法来管理数据来实现表视图,这些方法将被子类化以创建不同的表视图

这种方法完全有效,但它混合了管理数据的功能和显示功能,并且不是很灵活。一旦你说过widget,就是一个以某种方式访问​​数据的表视图,这一切都是一成不变的。如果您希望窗口小部件X以不同的方式获取其数据,则必须在UI类中构建该灵活性或将其销毁并从不同的子类重新创建它。

另一方面,委托模式允许NSTableView通过简单的赋值和重新加载完全更改其数据源。此外,UI类不会受到数据处理代码的污染,并且数据处理类不会受到UI处理代码的污染。

您可以在不使用协议的情况下进行委托,事实上,在10.5之前的Cocoa版本中,这是常态而不是异常,但是,如果您使用协议来定义委托API,您将获得编译器支持以进行错误检查您的代理实施和最低级别的文档。

答案 2 :(得分:0)

问题在于概括。最好避免无限制的进口。

想象一下这种情况。你有一个计时器(来自一个名为KDTimer的类)运行,并在某个时候到期。现在你想在3种游戏模式A,B和C中使用它(它们都对应3种不同的类)。现在,不同的游戏模式将对计时器的到期作出不同的反应。你得到的差异是:

<强>代表:

您在KDTimer中定义协议,将a,b或c设置为其委托(实际上a,b或c将其自身设置为定时器委托),然后执行

-(void) increaseProgress {
//incrementProgress
if(self.progress>=1) 
[_delegate timeExpired];
}

你是金色的。

<强>非代表:

#import "A"
#import "B"
#import "C"

@synthesize a,b,c;
-(void) increaseProgress {
//incrementProgress
if(self.progress>=1) 
[self.a timeExpired];
[self.b timeExpired];
[self.c timeExpired];
//we don't really know which instance should be messaged
}

关键是,不使用委托会导致代码复杂,通过使用它们,您可以获得更多可重用的组件。计时器不需要知道游戏模式,它只是试图告诉它已经过期。