这些都是很好的问题,很高兴看到您正在进行这项研究,并且似乎在学习如何“正确地做到这一点”,而不是仅仅一起学习。
首先,我同意前面的答案,重点是在适当时(根据MVC设计模式)将数据放入模型对象中的重要性。通常,除非状态信息严格是“表示”数据,否则您通常希望避免将状态信息放入控制器中。
其次,请参阅Stanford演示文稿的第10页,以获取有关如何以编程方式将控制器推入导航控制器的示例。有关如何使用Interface Builder进行“可视化”操作的示例,请参阅本教程。
第三,也许是最重要的一点,请注意,如果您在“依赖注入”设计模式的背景下考虑斯坦福演示文稿中提到的“最佳实践”,则更容易理解。简而言之,这意味着您的控制器不应“查找”完成其工作所需的对象(例如,引用全局变量)。相反,您应该始终将那些依赖项“注入”到控制器中(即,通过方法传入其所需的对象)。
如果遵循依赖项注入模式,则控制器将是模块化且可重复使用的。而且,如果您考虑到斯坦福演讲者来自何处(例如,作为Apple员工,他们的工作是建立易于重用的类),那么可重用性和模块化是当务之急。他们提到的共享数据的所有最佳实践都是依赖注入的一部分。
那就是我的回应要点。如果有帮助,我将在下面提供一个示例,该示例将依赖项注入模式与控制器一起使用。
在视图控制器中使用依赖注入的示例
假设您正在构建一个屏幕,其中列出了几本书。用户可以挑选他/她想要购买的书,然后点击“结帐”按钮转到结帐屏幕。
为此,您可以创建一个BookPickerViewController类,该类控制并显示GUI / view对象。它会从哪里获得所有图书数据?假设它依赖于BookWarehouse对象。因此,现在您的控制器基本上是在模型对象(BookWarehouse)和GUI / view对象之间代理数据。换言之,BookWarehouse对象上的BookPickerViewController DEPENDS。
不要这样做:
@implementation BookPickerViewController
-(void) doSomething {
// I need to do something with the BookWarehouse so I'm going to look it up
// using the BookWarehouse class method (comparable to a global variable)
BookWarehouse *warehouse = [BookWarehouse getSingleton];
...
}
相反,应该这样注入依赖项:
@implementation BookPickerViewController
-(void) initWithWarehouse: (BookWarehouse*)warehouse {
// myBookWarehouse is an instance variable
myBookWarehouse = warehouse;
[myBookWarehouse retain];
}
-(void) doSomething {
// I need to do something with the BookWarehouse object which was
// injected for me
[myBookWarehouse listBooks];
...
}
当苹果公司的人在谈论使用委托模式来“沟通支持层次结构”时,他们仍然在谈论依赖注入。在此示例中,一旦用户选择了他/她的书并准备退房,BookPickerViewController应该怎么做?好吧,这并不是真正的工作。它应该将该工作委托给其他对象,这意味着它依赖于另一个对象。因此,我们可以如下修改BookPickerViewController的init方法:
@implementation BookPickerViewController
-(void) initWithWarehouse: (BookWarehouse*)warehouse
andCheckoutController:(CheckoutController*)checkoutController
{
myBookWarehouse = warehouse;
myCheckoutController = checkoutController;
}
-(void) handleCheckout {
// We've collected the user's book picks in a "bookPicks" variable
[myCheckoutController handleCheckout: bookPicks];
...
}
所有这些的最终结果是,您可以给我您的BookPickerViewController类(以及相关的GUI / view对象),并且可以轻松地在自己的应用程序中使用它,假设BookWarehouse和CheckoutController是我可以实现的通用接口(即协议) :
@interface MyBookWarehouse : NSObject <BookWarehouse> { ... } @end
@implementation MyBookWarehouse { ... } @end
@interface MyCheckoutController : NSObject <CheckoutController> { ... } @end
@implementation MyCheckoutController { ... } @end
...
-(void) applicationDidFinishLoading {
MyBookWarehouse *myWarehouse = [[MyBookWarehouse alloc]init];
MyCheckoutController *myCheckout = [[MyCheckoutController alloc]init];
BookPickerViewController *bookPicker = [[BookPickerViewController alloc]
initWithWarehouse:myWarehouse
andCheckoutController:myCheckout];
...
[window addSubview:[bookPicker view]];
[window makeKeyAndVisible];
}
最后,您的BookPickerController不仅可重用,而且更易于测试。
-(void) testBookPickerController {
MockBookWarehouse *myWarehouse = [[MockBookWarehouse alloc]init];
MockCheckoutController *myCheckout = [[MockCheckoutController alloc]init];
BookPickerViewController *bookPicker = [[BookPickerViewController alloc] initWithWarehouse:myWarehouse andCheckoutController:myCheckout];
...
[bookPicker handleCheckout];
// Do stuff to verify that BookPickerViewController correctly called
// MockCheckoutController's handleCheckout: method and passed it a valid
// list of books
...
}