委托vs Unwind Segue将数据传递给父场景

时间:2013-10-24 02:52:13

标签: ios objective-c delegates segue

从iOS 6开始,展开的segue可用于向上导航场景层次结构。我试图决定将数据传递给父视图控制器的更清洁/更好/首选/更可维护的方法。从技术角度来看,有一些问题可以解决这个问题(例如“如果我有一个放松,我还需要一个代表”)但我找不到太多可以解决利弊的问题。

选项1:使用代表。

  • 通过传递父视图控制器作为遵守协议的委托来完成。
    • Child调用协议方法返回数据。
    • 如果Parent需要进行数据验证,则返回允许子处理错误所需的值/ dict。
  • 开销:协议定义和父级中的一种方法(用于数据验证和接收)。

选项2:使用展开segue

  • 通过调用孩子的unwind segue完成。
    • Child通过将按钮或故事板本身拖到Exit并将segue命名为performSegueWithIdentifier:sender
    • 来在场景中添加segue
    • 父实现returnFromSegueName(链接到该segue的用户名为方法)从孩子那里获取数据。
    • 数据验证虽然只能通过实施canPerformUnwindSegueAction:fromViewController:withSender来实现
      • 数据验证失败将需要Child上的另一个属性,因为此方法仅接受BOOL作为返回值。
  • 开销:两种方法,一个额外的属性,以及故事板恶作剧。

总的来说,代表们感觉更干净,但也许过时了。我倾向于朝那个方向倾斜吗?

2 个答案:

答案 0 :(得分:11)

我现在意识到这不是一个真正的问题,除了说这两种方法都没有错 - 它们都有其优点和缺点。在解决了一周并完成了关于这个主题的更多阅读之后,我至少可以量化为什么你可能想要使用unwind segue或者委托来在视图控制器之间工作。

耦合

两种模型大致相同(松散)耦合。在引擎盖下,展开segue只是一个代表,iOS已经完成了为您配线的工作。对于代理人,父母知道并遵守子协议。对于展开segue,父必须连接到故事板上的子节点以进行展开,并且需要知道子节点的属性以提取返回数据。但是,如果您是新来的代理人并且只想从子视图中获取一些数据,则展开segue可能不如使用具有委托的协议那样令人生畏。

灵活性

如果子到父交互的唯一目的是返回数据,则展开segues只是一个不错的选择。似乎没有办法取消正在进行的展开segue。因此,如果父级必须进行任何数据验证,或者子级需要与父级进行多次交互,则执行此操作的唯一方法是使用委托,其中可以将多个方法调用回父级。

可维护性

如果返回的数据的类型或其他方面发生变化,则更新展开segue会更容易,因为您只需要更新展开segue中的代码以查看新属性。对于协议/委托方法,您必须更新子级中的协议和父级中的实现。但是,unwind segue的简单性是由于您没有编译器检查您的合同(协议)而您可能很容易错过需要更新的父视图控制器中的位置。

获胜者

没有一个。您选择哪种方式取决于您的数据需求,协议的舒适程度(乍一看它们看起来比它们应该更加令人生畏),应用程序的复杂性以及长期维护需求。

就我的目的而言,我最后使用了代表,因为在某些情况下,我的孩子不得不多次回电给父母。但是,在我有许多数据要传回来的情况下,我采用了从unwind segue中学到的东西,并简单地使用了父级可以从中提取所需信息的子属性。我还使用它作为父方向孩子提供错误信息的便捷路径。为了与编程合作伙伴保持一致,我不会在程序中混合和匹配unwind segue与代表,但如果你愿意的话,没有理由你不能这样做。

答案 1 :(得分:7)

我对故事板非常怀疑,但我决定潜入并在新项目中使用它们。我很惊讶你可以轻松地在两个视图控制器之间进行通信。执行performSegueWithIdentifier时,您将获得新ViewController的句柄。您可以非常干净,漂亮地在新的viewController中设置所需的任何公开属性。

以下是一个例子:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([[segue identifier] isEqualToString:@"showDetail"]) {
        NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
        Student *student = [self.students objectAtIndex:indexPath.row + [self rowAdjuster]];
        [[segue destinationViewController] setStudent:student];
    } 
}

非常好,整洁。没有您需要跟踪或维护的特殊协议。

然后回来(我的IBAction连接到我的详细视图中的按钮)您可以再次获得对要返回的viewController的干净引用,并对该viewController进行操作。

- (IBAction)returnWithStudent:(UIStoryboardSegue *)segue {
    UIViewController *vc = [segue sourceViewController];
    if ([vc isKindOfClass:[ AddStudentViewController class]]) {
        AddStudentViewController *addViewController = (AddStudentViewController *)vc;
        if (addViewController.student != nil) {
            if ([addViewController hasTakenPhoto]) {
                [PhotoHelpers saveImageForStudent:addViewController.student];
            }
            [StudentController updateStudent:addViewController.student];
        }
    }
}

segue逻辑控制也很好。可以在shouldPerformSegue中执行逻辑检查,这非常方便。

我已经看到很多垃圾代码使用了“将某些内容发送给调用者”的协议,这些协议在耦合类时非常差。它做了三向安排 - viewController1 - >协议 - > viewController2,而segues是viewController1-> viewController2的一个很好的安排。

segue是一种干净而独特地结合这两个类的好方法。我强烈推荐它。