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

El *_*Tea 12 delegates objective-c ios segue

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

选项1:使用代表.

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

选项2:使用展开segue

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

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

El *_*Tea 11

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

耦合

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

灵活性

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

可维护性

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

获胜者,冠军

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

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

  • 我还没有完全解决的另一个考虑因素:它看起来像展开segues允许你放松多次推送以一次性导航回多个级别.我认为这也可以通过其他方式实现,但是通过展开segues看起来更容易. (3认同)

Hal*_*alR 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];
    } 
}
Run Code Online (Sandbox Code Playgroud)

这是非常好,整洁.您无需跟踪或维护特殊协议.

然后回来(我的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];
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

我已经看到很多垃圾代码使用"发送回调用者的东西"的协议,这些协议在耦合类时非常差.它做了一个三向排列 - viewController1 - > protocol - > viewController2,而segues对viewController1-> viewController2进行了很好的排列.

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