如果你是 Objective-C / Cocoa Touch 的重度使用者,那么你一定被一个东西困扰过:不同的 View Controller 之间,如果互相进行沟通?
举个常见的例子。假设我们手头上有一个 UIViewController 实例,名叫 parentViewController ,在这个 parentViewController 生命周期的某个节点,它需要以 modal view controller 的形式 present 一个 UIViewController 实例。这个将要被 present 的 UIViewController 实例名叫 childViewController。
也就是说,在 parentViewController 的代码中,我们应该会看到以下这句话。
[self presentModalViewController:childViewController animated:YES];
以上的描述和代码都非常的顺其自然。然而我们很快会发现一个问题,当这个 childViewController 需要被 dismiss 时,由哪个 view controller 实例负责 dismiss ?
此时答案不唯一。我们有两种选择,两种都可行,但有区别。
第一种选择,由 childViewController 自己负责,把自己给 dismiss 掉。代码很简单。
[self dismissModalViewControllerAnimated:YES];
第二种选择,由 parentViewController 负责,把 childViewController 给 dismiss 掉。代码要复杂一些,分两步。
- childViewController 通知 parentViewController ,请求 dismiss 。
- parentViewController 收到通知,执行 dismiss , childViewController 生命周期结束。
“第一种和第二种看似区别不大,而且第一种方法的代码量很小。我们就采用第一种,忘掉第二种吧。”
别。千万别。第一种虽然很多时候不致于给你添 Error ,但是非常危险。你应该忘掉第一种,记住第二种。实际上,第一种做法是钻了 Cocoa Touch 的空子,虽然偶尔能用而且不致于添 Error ,但是非常不规范,应当摒弃。这两种方式的区别,具体来说。
- 第一种方式,由 childViewController 自行 dismiss 掉自己,并不通知 parentViewController。这导致 parentViewController 虽然拥有 present 下属 childViewController 的权利,但一旦 present 结束,便完全失去了对 childViewController 控制。下一秒钟, childViewController
是否仍然存在,它所含有的成员变量是否能够被正常访问, parentViewController 将会一无所知。 - 大多数情况下, childViewController 完事后,需要和 parentViewController 进行沟通,在自己生命周期结束之际,将由自己负责采集、计算的信息交给 parentViewController 保管(retain / copy)。很明显,第一种方式完全无法实现这个“托管”的功能。
第二种方式其实有一个很正式的名字, Delegate Design Pattern in Objective-C 。没错,它只是一种 Design Pattern ,是一种编程风格,并不是你在代码里必须要做的事情。但是这种编程风格实在是很实用,以至于被 Xcode 的官方模板 Utility Application 所直接采用。 Delegate Design Pattern 看上去很复杂,实现起来其实不难,格式非常固定。我们看看 Utility Application 是怎么做的。
这是 Utility Application 中 FlipsideViewController 对应的 .h 文件。 FlipsideViewController 对应于我们上面描述里的 childViewController 。
#import <UIKit/UIKit.h> @protocol FlipsideViewControllerDelegate; @interface FlipsideViewController : UIViewController { id <FlipsideViewControllerDelegate> delegate; } @property (nonatomic, assign) id <FlipsideViewControllerDelegate> delegate; - (IBAction)done:(id)sender; @end @protocol FlipsideViewControllerDelegate - (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller; @end
从上面的代码里,我们不难发现,在 childViewController 的层面上,实现 Delegate Design Pattern 的方法其实很固定,总结一下,有这么几个要点。
- 我们首先需要声明一个协议,如上面代码中的 FlipsideViewControllerDelegate 协议。有了这个协议,我们就能确保,负责 present 我们的 FlipsideViewController 的 parentViewController 一定会实现flipsideViewControllerDidFinish: 这个用来“临终托管” FlipsideViewController 的方法。(否则编译时我们会得到一个
Warning ) - 我们需要有一个 property 属性为 assign 的指针。这个指针由 FlipsideViewController 所有,用来记录自己的 parent 是谁。注意,属性必须为 assign 而不能是 retain 或者 copy 。(否则。。你的内存管理会很难看。。)
剩下的事情就简单了。当我们的 FlipsideViewController 被初次 alloc 和 init 后,将它的 delegate 指向将要 present 它的 parentViewController 。于此同时,我们需要确保 parentViewController 实现 flipsideViewControllerDidFinish: 函数,因为这个函数将是 FlipsideViewController 和 parentViewController “临终通讯”的接口。然后,我们就没事了。下一次
FlipsideViewController 生命周期将要结束时,它的 delegate ,也就是 parentViewController 将会及时收到通知,并负责保存信息和移除 FlipsideViewController 。借助 Delegate 风格的帮助,整个流程都将变得非常干净利索。
转载地址:http://www.diwublog.com/archives/130