Я согласен, что иногда кажется, что 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;
}