Подтвердить что ты не робот

Импорт AppDelegate

Часто я инициализирую свои переменные класса модели в своем AppDelegate, чтобы их можно было использовать разными ViewControllers, не передавая их экземпляр через файлы классов. Тем не менее, каждый раз, когда я импортирую AppDelegate в файл .m для доступа к этим переменным данным, я чувствую, что делаю некоторые ошибки.

Является ли это правильным способом доступа к этим переменным или мне нужно делать что-то по-другому?

EDIT: Моя проблема заключается не в доступе к переменным. В настоящее время я использую эту строку кода для получения экземпляра appDelegate:

id appDelegate = [[UIApplication sharedApplication] delegate];

Концептуально, я хочу знать, является ли это приемлемым способом взаимодействия с классами моделей приложений. Мне кажется, что приложение AppDelegate управляет приложением в целом. Таким образом, кажется, неинтересно импортировать этот класс в другие классы дальше по цепочке классов приложений.

4b9b3361

Ответ 1

Является ли это правильным способом доступа к этим переменным или должен ли я делать что-то по-другому?

Вы найдете, что разные люди имеют разные мнения об этом. Стиль, который я предпочитаю, состоит в том, чтобы делегат приложения передал необходимую информацию первому контроллеру представления, и этот контроллер представления передал его всем контроллерам представлений, которые он создает, и так далее. Основной причиной этого является то, что он не позволяет контроллерам дочерних представлений зависать от вещей, о которых они не знают о бизнесе.

Если у вас есть редактор деталей, например, вы хотите передать этот редактор именно тем, что ему нужно для выполнения своей работы. Если вы дадите эту информацию, редактор будет полностью гибким - он отредактирует любую информацию, которую вы ему дадите. Если редактор знает, что он должен получать свои данные от какого-то внешнего объекта, например, делегата приложения, тогда он теряет определенную степень гибкости - он может получать данные только от того, о чем он знает.

Итак, отлично, чтобы настроить вашу модель данных в делегате приложения. Но когда дело доходит до предоставления доступа к модели, подумайте: сказать, не спрашивайте. То есть, делегат приложения скажет первому контроллеру представления, какой объект модели использовать, и попросите диспетчера рассказать о следующем, и так далее. Если вы должны спросить, вы должны знать, с кем спросить, и что там, где зависимости начинаются в неправильном направлении.

каждый раз, когда я импортирую AppDelegate в файл .m для доступа к этим переменные данные Я чувствую, что я делаю некоторые ошибки.

Доверяй этому инстинкту. Подумайте, почему это неправильно.

Ответ 2

Я согласен, что иногда кажется, что AppDelegate - это логическое место, где можно разместить вещи, которые вы хотите реализовать один раз, но может потребоваться из нескольких мест. Создание синглтона для каждого из них прекрасно, если эти вещи сложны, но он создает много дополнительных файлов и путаницу в проекте. Я также согласен с большинством ответов здесь, что построение зависимостей от AppDelegate является действительно плохим дизайном.

Я считаю, что лучшим решением является создание Протокола! Затем поместите свойство IBOutlet в свойство, чтобы сделать то, что вам нужно сделать в каждом из контроллеров, которым нужна эта функция. Протоколы - стандартный способ objective-C для расцепления классов.

Итак, в качестве примера, возможно, у меня есть URL-адрес базы данных, который мне может понадобиться из нескольких мест. Вероятно, лучшим способом было бы установить свойство с ним каждый шаг по пути. Но в некоторых ситуациях, которые могут быть громоздкими из-за использования контроллера запаса и не хотят подклассифицировать его. Вот мое решение:

Создать файл: MainDatabasePovider.h

#import <Foundation/Foundation.h>
@protocol MainDatabaseProvider <NSObject>
@required
@property (nonatomic, readonly) NSURL *applicationDocumentsDirectory;
@property (nonatomic, weak) NSURL *mainDatabase;
@end

Теперь "любой" (т.е. любой класс), который говорит, что он реализует протокол MainDatabaseProvder, гарантированно предоставит два вышеуказанных метода. Это может быть объект AppDelegate или ЛЮБОЙ.

Теперь, если я хочу, чтобы мой AppDelegate предоставил информацию, я изменяю файл AppDelegate.h, чтобы иметь:

#import "MainDatabaseProvider.h"

@interface AppDelegate : UIResponder <UIApplicationDelegate, MainDatabaseProvider>
@property (strong, nonatomic) UIWindow *window;
@end

(Единственное изменение заключается в добавлении протокола MainDatabaseProvider к строке @inteface, это может быть сделано для класса ЛЮБОЙ, который вы хотите предоставить).

В моем AppDelegate.m файле я должен написать два метода...

@implementation AppDelegate
      ...
@synthesize mainDatabase = _mainDatabase;
      ...
- (NSURL *) applicationDocumentsDirectory {
    return [[[NSFileManager defaultManager] URLsForDirectory: NSDocumentDirectory inDomains: NSUserDomainMask] lastObject];
}
- (void) setMainDatabase: (NSURL *) mainDatabase {
    if( _mainDatabase != mainDatabase ) {
        _mainDatabase = mainDatabase;
    }
}
- (NSURL *) mainDatabase {
    if( !_mainDatabase ) {
        NSURL *docURL = self.applicationDocumentsDirectory;
        self.mainDatabase = [docURL URLByAppendingPathComponent: @"My Great Database"];
    }
    return _mainDatabase;
}
      ...
@end

Теперь в моих контроллерах или других классах, которые нуждаются в получении MainDatabase, я добавляю следующее:

В своих файлах .h:

#import "MainDatabaseProvider.h"
      ...
@interface myGreatViewController: UIViewController
@property (nonatomic, weak) IBOutlet id <MainDatabaseProvider> mainDatabaseProvider;
      ...
@end

Это свойство может быть установлено в ранее известном как InterfaceBuilder путем перетаскивания или может быть установлено в коде в prepareForSegue или мне нравится предоставлять пользовательский getter, который по умолчанию равен AppDelegate в случае я ленивы или забывчивы и не делайте ни одного из вышеперечисленных. В файле .m будет выглядеть:

@implementation myGreatViewController
@synthesize mainDatabaseProvider = _mainDatabaseProvider;
      ...
- (id <MainDatabaseProvider>) mainDatabaseProvider {
    id appDelegate = [[UIApplication sharedApplication] delegate];
    if( !_mainDatabaseProvider && [appDelegate conformsToProtocol: @protocol(MainDatabaseProvider)] )
        return appDelegate;
    return _mainDatabaseProvider;
}
// To get the database URL you would just do something like...
- (void) viewWillAppear: (BOOL) animated {
    NSLog( @"In %s the mainDatabaseProvider says the main database is \"%@\"", __func__, self.mainDatabaseProvider.mainDatabase.path );
}

Теперь mainDatabaseProvider может быть ЛЮБОЙ. У меня есть возможность установить его в InterfaceBuilder или my StoryBoard (хотя я действительно не думаю, что это элемент пользовательского интерфейса, поэтому я, как правило, не был бы, но это довольно типично для этого) Я могу установить его в коде вне моего контроллера, прежде чем он загрузится в prepareForSegue:sender: или tableView:didSelectRowAtIndexPath:. И если я не установлю его вообще, он по умолчанию будет AppDelegate, если я его правильно настроил.

Я могу даже поставить некоторые безопасные охранники, когда я забываю делать что-то в моем старости, изменяя приведенный выше getter, чтобы помочь мне с чем-то вроде:

- (id <MainDatabaseProvider>) mainDatabaseProvider {
    if( !_mainDatabaseProvider ) {
        id appDelegate = [[UIApplication sharedApplication] delegate];
        if( ![appDelegate conformsToProtocol: @protocol(MainDatabaseProvider)] ) {
            NSLog( @"Hey!! The mainDatabaseProvider is not set and the AppDelegate does not conform to the MainDatabaseProvider protocol. How do you expect me to figure out where the database is!" );
        } else {
            return appDelegate;
    }
    return _mainDatabaseProvider;
}

Ответ 3

Вам следует серьезно избегать импорта AppDelegate повсюду, и вы должны чувствовать, что каждый раз делаете что-то неправильно (+1 для этого). Вы по существу создаете Большой шар грязи и должны пересмотреть свой дизайн. Если, например, вы используете CoreData для своих моделей, рассмотрите структуру, такую ​​как Magical Panda Active Record для извлечения данных. Я работаю над корпоративным приложением, а AppDelegate.h включает только AppDelegate.m.

Ответ 4

Я тоже импортирую их и использую их следующим образом:

 AppDelegate *delegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
 [delegate variable];

Другим способом может быть использование Singleton.

Ответ 5

Поместите этот метод в класс AppDelegate

+ (AppDelegate *)get {
    return (AppDelegate *) [[UIApplication sharedApplication] delegate];
}

И когда вам нужно вызвать приложение AppDelegate:

[AppDelegate get];

Ответ 6

Это один из способов сделать это, да, однако это не очень элегантно. Синглтоны тоже есть, да, но не очень элегантно:) - и reeealy НЕ легко проверить ваш код, если вам нужно издеваться над всеми вашими синглонами. Вместо этого я, вероятно, хотел бы сделать один синглтон для поставщика услуг и спросить этого поставщика услуг для экземпляра вашего поставщика модели.

Скажем, ваш класс поставщика услуг - это одноэлементный номер, и вам нужно получить доступ к модели для получения сведений о пользователе. Я сделал бы это следующим образом:

JMUserDetailModel *myModel = [[[JMServiceProvider sharedInstance] modelProvider] userDetailModel];

Это означает, что вы создадите класс JMServiceProvider для регистрации служб и сможете получить эти службы. Эти службы действуют как одноэлементный, однако, если вам нужен unit test ваш код, то регистрация другой службы, действующей так же, как оригинал, является просто куском пирога.

Надеюсь, это ответит на ваш вопрос.

EDIT: И прочитайте эту статью: http://martinfowler.com/articles/injection.html - очень хороший, объясняющий также сервисно-ориентированные архитектуры...

Ответ 7

Посмотрите на одиночные игры. Они могут быть более элегантным способом управления глобальными данными.