импорт AppDelegate - PullRequest
       42

импорт AppDelegate

18 голосов
/ 07 декабря 2011

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

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

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

id appDelegate = [[UIApplication sharedApplication] delegate];

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

Ответы [ 7 ]

15 голосов
/ 07 декабря 2011

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

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

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

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

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

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

9 голосов
/ 24 марта 2012

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

Я думаю, что лучшее решение - это создать протокол !Затем добавьте IBOutlet к свойству, чтобы сделать то, что вам нужно сделать в каждом контроллере, для которого требуется эта функция. Протоколы являются стандартным способом target-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 или я хотел бы предоставить пользовательский метод получения, который по умолчанию равен 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 или в моем 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 голосов
/ 07 декабря 2011

Вы должны серьезно избегать импорта AppDelegate везде, и вы должны чувствовать, что делаете что-то неправильно каждый раз, когда вы делаете это (+1 за это).По сути, вы создаете Big Ball of Mud и должны пересмотреть свой дизайн.Например, если вы используете CoreData для своих моделей, рассмотрите такую ​​среду, как Активная запись магической панды для извлечения данных.Я работаю над корпоративным приложением, и AppDelegate.h входит только в AppDelegate.m.

2 голосов
/ 07 декабря 2011

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

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

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

1 голос
/ 08 декабря 2011

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

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

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

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

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

РЕДАКТИРОВАТЬ: И прочитать эту статью: http://martinfowler.com/articles/injection.html - очень хорошая, объясняющая также сервис-ориентированные архитектуры ...

1 голос
/ 07 декабря 2011

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

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

А когда вам нужно вызвать свой AppDelegate, используйте:

[AppDelegate get];
0 голосов
/ 08 декабря 2011

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

...