Delegates 为什么要使用委托和协议,而不只是在Swift中传递实例?

Delegates 为什么要使用委托和协议,而不只是在Swift中传递实例?,delegates,swift,protocols,Delegates,Swift,Protocols,我试图在Swift中在视图之间传递变量,遇到了协议和委托这一相当抽象的概念 然后我尝试在第二个视图中存储对第一个视图的引用,并直接调用该视图上的函数。这似乎有效: 屏幕1 屏幕2 我的问题:这个代码有什么问题?如果可以直接传递引用,为什么要使用委托和协议 可能相关:什么时候视图被取消初始化并替换为一个全新的视图实例?我是在旧实例上调用“getName()”吗?基本的设计原则是不公开任何超出您必须公开的设计。通过在周围传递引用,可以公开整个对象。这意味着其他人可以调用它的任何函数并访问它的任何属性

我试图在Swift中在视图之间传递变量,遇到了协议和委托这一相当抽象的概念

然后我尝试在第二个视图中存储对第一个视图的引用,并直接调用该视图上的函数。这似乎有效:

屏幕1

屏幕2

我的问题:这个代码有什么问题?如果可以直接传递引用,为什么要使用委托和协议


可能相关:什么时候视图被取消初始化并替换为一个全新的视图实例?我是在旧实例上调用“getName()”吗?

基本的设计原则是不公开任何超出您必须公开的设计。通过在周围传递引用,可以公开整个对象。这意味着其他人可以调用它的任何函数并访问它的任何属性。并改变它们。这不好。除了让其他人以它可能不打算的方式使用该对象外,如果您试图在将来更改该对象,并且发现它破坏了使用您不打算的对象的其他人,您也会遇到问题。所以,最好不要暴露任何你不需要的东西。这是委托和协议的目的。它使对象能够完全控制暴露的内容。更安全。更好的设计。

这主要是一个意见问题,所以这个问题可能应该结束,但我认为开发人员社区作为一个整体对此达成了一致,所以我无论如何都要回答它

(代码结构设计)中的一个重要概念称为。基本原则是,您应该将代码必须完成的工作分解为只有一个明确目的的小组件。这些组件中的每一个都应该能够独立运行,而不需要太多关注其他组件,除了需要直接与之交互的组件之外

这对代码重用有很大帮助。如果您设计了一个独立于大多数/如果不是所有其他组件的小组件,那么您可以轻松地将其插入到代码的其他部分或其他应用程序中。以UITableView为例。通过使用委托模式,每个开发人员都可以轻松地创建一个表视图,并用他们想要的任何数据填充它。由于该数据源是一个单独的对象(单独关注数据的产生),所以可以将该数据源附加到多个表视图。想想iOS上的联系人列表。您将希望以多种方式访问相同的数据。与总是重写加载特定数据并以特定方式显示数据的表视图不同,您可以使用不同的表视图多次重用数据源

这也有助于提高代码的可理解性。开发者很难对你的应用程序状态保持太多的了解。如果您的每一个代码组件都被分解为小的、定义良好的职责,那么开发人员可以分别理解每个组件。他们还可以查看组件,并对其功能做出准确的假设,而无需查看具体的实现。这对于小型应用程序来说并不是什么大问题,但随着代码库的增长,这一点变得非常重要

通过传递对第一个视图控制器的引用,可以使第二个视图控制器完全依赖于第一个视图控制器。您不能在另一个实例中重用第二个视图控制器,其作业将变得不那么清晰


分离关注点还有很多其他好处,但我相信这是两个引人注目且重要的好处。

我认为您没有完全理解什么是协议

我总是说协议就像合同。
实现特定协议的委托对象承诺它可以做委托人不能做的事情

在现实世界中,我的房子的管子有问题。
我(委托人)打电话给水管工(委托人)修理它。管道工(通过合同)保证能够修理它。承诺就是协议。我不在乎他怎么做,只要他做就行

但这些合同不仅对授权有用。
我正在写一个食品订购应用程序。因为它有一个菜单,所以需要在其中显示一个项目。
我可以使用基本继承编写一个类MenuItem,所有子类都必须从中继承。
或者我写一个协议来表达:«无论你是什么目标,只要你履行这个合同,我们就有交易»。这允许我创建许多不同的类或在类别中注释现有的类,尽管我没有多重继承的工具

实际上,我同时做这两件事:我编写了一个协议
MenuItem
和一个符合协议的类
MenuItem
。现在我可以使用简单继承,也可以使用不从类
MenuItem
继承的类

Objective-C中的代码(抱歉:我仍在转换为Swift)

苹果对NSObject使用相同的架构:有一个协议和一个类
NSObject
。这允许从类
NSObject
继承的非完整类充当NSObject。一个著名的例子:
NSProxy


在您的情况下,Screen1承诺能够理解细节视图控制器Screen2发送的消息。这些允许解耦:任何理解Screen1协议的对象都可以使用。它还有助于维护一个健全的对象树,因为我们不需要循环导入。但一般来说,您必须记住,委托人(Screen2)必须保留对其委托人的弱引用,否则我们会有一个保留圈


当然,一个重要的例子是it UITableView:
table view对象了解有关渲染其单元格、处理滚动等的所有信息。但是编写它的工程师现在不知道您希望表视图是什么样子。这就是为什么他引入了del
class Screen1: UIViewController {

    var myName = "Screen1"

    override func viewDidLoad() {
        super.viewDidLoad()
    }
    //
    // checking if the segue to screen 2 is called and then passing a reference
    //
    override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
        if segue.identifier == "screen2Segue"{
            let vc = segue.destinationViewController as Screen2
            vc.storedReference = self
        }
    }

    func getName() -> String {
        return myName
    }
}
class Screen2: UIViewController {

    var storedReference:Screen1!

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    func testReference() {
        // calling a function on the stored reference to screen 1
        var str = storedReference.getName()
        println("Leaving screen 2, going to " + str)
    }
}
@protocol MenuItem <NSObject>

-(NSString *)name;
-(double) price;
-(UIColor *)itemColor;

@end


@interface MenuItem : NSObject <MenuItem>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) double price;
@property (nonatomic, strong) UIColor *itemColor;

@end
#import "MenuItem.h"

@implementation MenuItem

-(id)initWithCoder:(NSCoder *)decoder
{
    self = [super init];
    if (self) {
        self.name = [decoder decodeObjectForKey:@"name"];
        self.price = [decoder decodeDoubleForKey:@"price"];
        self.itemColor = [decoder decodeObjectForKey:@"itemColor"];
    }
    return self;
}

-(void)encodeWithCoder:(NSCoder *)encoder
{
    [encoder encodeDouble:self.price forKey:@"price"];
    [encoder encodeObject:self.name forKey:@"name"];
    [encoder encodeObject:self.itemColor forKey:@"itemColor"];
}


@end