我同意,有时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 或 ANY 对象。
现在,如果我希望我的 AppDelegate 提供信息,我将 AppDelegate.h 文件更改为:
#import "MainDatabaseProvider.h"
@interface AppDelegate : UIResponder <UIApplicationDelegate, MainDatabaseProvider>
@property (strong, nonatomic) UIWindow *window;
@end
(唯一的改变是将 MainDatabaseProvider 协议添加到 @inteface 行,这可以再次添加到您想要提供该功能的 ANY 类) .
在我的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 可以是ANY 对象。我可以在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;
}