Это хорошие вопросы, и приятно видеть, что вы проводите это исследование и, похоже, заинтересованы в том, чтобы научиться «делать это правильно», а не просто взламывать его вместе.
Первый . Я согласен с предыдущими ответами, в которых основное внимание уделяется важности помещения данных в объекты модели, когда это необходимо (согласно шаблону проектирования MVC). Обычно вы хотите избежать размещения информации о состоянии внутри контроллера, если это не строго данные представления.
Секунда , см. Стр. 10 презентации Стэнфорда для примера того, как программно вставить контроллер в контроллер навигации. Для примера того, как сделать это «визуально» с помощью Interface Builder, взгляните на это руководство .
Третий и, возможно, самое важное, отметьте, что "лучшие практики", упомянутые в презентации Стэнфорда, намного легче понять, если вы думаете о них в контексте " Внедрение зависимости "шаблон проектирования. Короче говоря, это означает, что ваш контроллер не должен «искать» объекты, необходимые для его работы (например, ссылаться на глобальную переменную). Вместо этого вы всегда должны «вводить» эти зависимости в контроллер (т.е. передавать нужные ему объекты с помощью методов).
Если вы следуете шаблону внедрения зависимостей, ваш контроллер будет модульным и может использоваться повторно. И если вы думаете о том, откуда берутся докладчики из Стэнфорда (то есть, когда сотрудники Apple занимаются созданием классов, которые можно легко использовать повторно), многократное использование и модульность являются первоочередными задачами. Все лучшие практики, которые они упоминают для обмена данными, являются частью внедрения зависимости.
Это суть моего ответа. Ниже приведен пример использования шаблона внедрения зависимостей с контроллером на случай, если это будет полезно.
Пример использования внедрения зависимости с контроллером представления
Допустим, вы создаете экран, на котором перечислены несколько книг. Пользователь может выбрать книги, которые он / она хочет купить, а затем нажать кнопку «Оформить заказ», чтобы перейти на экран оформления заказа.
Чтобы построить это, вы можете создать класс BookPickerViewController, который управляет и отображает объекты GUI / представления. Где он получит все данные книги? Допустим, это зависит от объекта BookWarehouse. Итак, теперь ваш контроллер в основном выполняет посреднические функции между объектом модели (BookWarehouse) и объектами GUI / представления. Другими словами, BookPickerViewController ЗАВИСИТ от объекта BookWarehouse.
Не делай этого:
@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];
...
}
Когда парни из Apple говорят об использовании шаблона делегирования для «обратной связи по иерархии», они все еще говорят о внедрении зависимости. В этом примере, что должен делать BookPickerViewController, как только пользователь выбрал свои книги и готов их проверить? Ну, это не совсем его работа. Он должен ДЕЛЕГАТИЗИРОВАТЬ эту работу с другим объектом, что означает, что он ЗАВИСИТ от другого объекта. Поэтому мы можем изменить наш метод инициализации BookPickerViewController следующим образом:
@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 / представления), и я могу легко использовать его в своем собственном приложении, предполагая, что 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
...
}