数据源和代理之间有什么区别?

44

我有一个关于Cocoa框架设计模式的基本问题。

代理(Delegate)和数据源(Data source)有什么区别?

它们都可以使用 @protocols 声明,但有些类或框架使用 delegate,而另一些则使用 datasource

UI/NSTableView 中我能理解的是,delegate 响应与 UI 相关的事件,而 datasource 纯粹与数据相关。但是,我不知道除了 Cocoa 的 UI 类以外是否还有其他数据源实现。

注意:

  • 我在这个问题中提到的代理并不总是与 UI 事件相关。
  • 数据源问题已经得到了回答。
6个回答

50

数据源提供数据,委托提供行为。

MVC中,数据源位于模型层,而委托则位于控制层。

实际上,仔细想想,数据源通常是较低的控制器,靠近模型。我认为我从未将模型对象用作我的数据源。


35

委托和数据源模式是彼此独立且正交的:

在Cocoa中,委托模式非常常见,允许委托(在OS X 10.6之前实现非正式委托协议的任意实例,或在10.6及以后实现正式委托@protocol的实例)修改对象实例的行为。该模式通常用于替代子类化:不是通过子类化类来改变其行为,而是提供一个响应适当方法的委托。使用委托的类在指定事件发生时向其委托发送消息。类与委托之间的API由类定义,并且对于使用模式的每个类都不同,但API通常包括询问委托如何处理特定事件的消息。相比子类化,委托模式的一个优点是一个类可以实现多个委托协议,允许其实例作为多个类的委托。同样,对象实例可以是多个其他对象的委托(因此大多数委托API将对象作为API中每个消息的第一个参数传递)。委托模式在其他UI框架中不太常见(虽然Qt在其Model/View框架中使用委托模式),并且与.Net/CLR委托不同,后者本质上是类型化的函数指针。

数据源模式通常由Cocoa中具有复杂状态数据(如NSBrowser、NSTableView、NSOutlineView等)的NSView子类使用。数据源协议定义了这些(和其他)类实例可能用来获取视图中要显示的数据的API。尽管NSController和Cocoa Bindings体系结构已经取代了许多数据源模式的用途,但它仍然很常见且非常强大。与上述委托模式一样,其部分功能的原因在于一个对象能够充当多个使用数据源的实例的数据源(甚至可能是具有不同数据源协议的多个类的实例)。数据源模式在其他UI框架中也很常见,例如Qt(在模型/视图框架中,模型类似于数据源)和WPF/Silverlight(其中数据源可能更密切地类似于视图模型)。


1
好的,非常全面的解释。我现在的理解是数据源不响应事件。在 Cocoa 类的情况下,它仅提供了一种实现在特定视图中显示哪些数据的方法。我只是不确定为什么数据源的概念在 Cocoa 框架的 UI 相关类之外从未被使用过。 - Jesse Armand
2
@Jesse 清楚起见,无论是代理还是数据源都不会直接响应UI事件(即通过运行循环传递给应用程序的NSEvent)。一个对象可能会询问代理如何响应事件(无论是NSEvent类型的事件还是其他对象的任何消息)。数据源在UI类之外并不常用,因为它们是不需要的;MVC模型是其自身的数据源。 - Barry Wark
好的,如果你误解了我的意思,我很抱歉。我并不是想说委托(delegate)总是响应UI事件。我主要关心的是数据源这个术语在UI类外部使用的"灵活性"。我同意,我从来没有发现它在UI类外部有任何需要。 - Jesse Armand
1
你使用的编程语言非常难懂。我认为只有有经验的人才能理解。 - Mihir Oza

14
假设你有三个表格视图,分别用于狗、猫和鸟。点击每个单元格将显示一个新屏幕,其中包含它的放大照片。
为了设计这个功能,你需要为狗、猫和鸟设计三个不同的数据源。基本上你需要三个数组。
然而,你不需要三个表格视图代理。因为表格视图的行为都是相同的。它们只是呈现一个视图控制器,并用UIImage填充它。这只有在代理以通用方式编写时才成立,即代理中没有特定于狗、猫或鸟的代码。
话虽如此,你可以从数据源中抽象出狗、猫和鸟,但我的答案只是一个人为的例子。一些自定义对象太复杂,无法使用相同的结构,因此需要三个数据源。
旧答案:
在回答问题之前,你必须更好地理解委托设计模式: 让我先问一个问题:
默认情况下,TableView 是这样的:

enter image description here

UITableView如何知道要呈现多少个单元格?每个单元格中呈现什么?

  • 它本身并不知道。
  • 它会向另一个类询问,以便将单元格的数量和要返回的单元格(例如单元格图像、单元格标题、单元格子标题等)值通知给自己。通常情况下,您会在ViewController(代理类)中看到一个tableView(委托类)。
  • 这种一个类询问另一个类的概念被称为委托!

现在您已经知道什么是委托,为了回答OP的实际问题:这主要涉及语义差异。
如果您只使用(而不是创建自己的协议)基础的代理和数据源,那么这对您来说并不重要。但是,如果您打算编写自定义协议,则了解它们将有助于更好地编写(以及更重要的是阅读、重构)代码。
从开发人员的角度来看,它们都处理委托类和代理类之间的交互。
数据源 一个数据源和代理几乎是相同的。不同之处在于与委托对象的关系。数据源被委派控制数据而非用户界面。委托对象通常是视图对象例如表视图,它持有对其数据源的引用并偶尔请求应显示的数据。数据源和委托一样必须采用协议并最少实现该协议的必需方法。数据源负责管理提供给委托视图的模型对象的内存。
通俗易懂的说:
数据源主要处理“什么”,通常在初始化时完成。委托主要处理“如何”,并向您提供一些参数以产生特定的行为,例如如果用户点击了这个…应该发生什么?如果他们滑动…应该发生什么?
以tableView为例:

数据源
它里面有什么?我正在呈现什么样的单元格?cellForRowAtIndexPath
章节的标题是什么?titleForHeaderInSection。 有多少个单元格?numberOfRowsInSection 因此,通常你会返回值。对于代理来说,更常见的类型是void


数据源方法

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell // return a cell ie UITableViewCell
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int // return a number ie an Int
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? // return the title ie a String  

委托方法
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
func tableView(tableView: UITableView, willBeginEditingRowAtIndexPath indexPath: NSIndexPath)
func tableView(tableView: UITableView, didEndEditingRowAtIndexPath indexPath: NSIndexPath)

我显然是有选择地进行了选择,因为有些数据源方法不返回,而有些委托方法确实会返回。

代理
在显示页脚后,我应该做什么/使用什么“行为形式”,您希望我弹出一个警告框吗?didEndDisplayingFooterView

我将拥有一种附加类型,可以为单元格提供一些额外的功能吗?accessoryTypeForRowWithIndexPath


7

在我看来,DataSource 是一个不知道数据存储位置的对象,因此您需要提供数据位置信息。比如告诉对象列中有多少项。

Delegate 是对象向您展示的一部分,必须由您的类实现,因为对象知道数据存储位置,但它不知道如何正确使用它。


6

简单来说:

Delegate与UI和用户对单元格和表格的操作相关。

常用方法:willSelectRow、didSelectRow、willDisplay、heightForRow、willBeginEditingAt。

Data Source负责编辑、填充和显示表视图中的数据。

常用方法:canEditRowAt、commit、titleForHeaderInSection、cellForRowAt、numberOfSections、sectionIndexTitles。


要获取更详细的信息,只需查阅文档,您就可以清楚地看到它们之间的区别。 - Modesto Cabrera

3

这两者都是协议,现在协议的主要目的是保持通用的编码规范,或者说对所有人使用相同的编码规范(据我所知)。假设我创建了一个没有UITableViewDataSourceUITableViewDelegate的tableView,我会以一种你不会的方式创建tableView。这就是协议的作用,苹果创建了一些规则或协议,每个人都必须遵循这些规则。现在DataSourceDelegate显然是协议,看到名字,你可以理解DataSource处理类似于tableView的numberOfRowsInSectioncellForRowAtIndexPathnumberOfSections之类的数据,而Delegate则处理tableView的didSelectRowwillSelectRowheightForRow等与UI变化/操作相关的内容。因此,这只是命名约定,没有什么假设来保持任务分离。正如@kubi之前所说:DataSource提供数据,Delegate提供行为。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接