导入 AppDelegate
Posted
技术标签:
【中文标题】导入 AppDelegate【英文标题】:importing AppDelegate 【发布时间】:2012-01-15 07:23:32 【问题描述】:我经常在我的 AppDelegate 中初始化我的模型类变量,这样它们就可以被不同的 ViewControllers 使用,而无需跨类文件传递它们的实例。但是,每次我将 AppDelegate 导入 .m 文件以访问这些变量的数据时,我都觉得自己做错了。
这是访问这些变量的正确方法还是我应该做一些不同的事情?
编辑: 我的问题不是如何访问变量。我目前使用这行代码来获取我的 appDelegate 实例:
id appDelegate = [[UIApplication sharedApplication] delegate];
从概念上讲,我想知道这是否是与应用程序模型类交互的公认方式。在我看来,应用程序的 AppDelegate 似乎管理着整个应用程序。因此,将这个类导入到应用程序类链下游的其他类中似乎违反直觉。
【问题讨论】:
+1 好问题和完全正确的直觉 - 相信自己并改变应用程序委托 mutilation 的习惯:D 【参考方案1】:我同意,有时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;
【讨论】:
+1 很好的答案。只是我正在寻找的解决方案。简而言之,您的建议是:1.有时需要使用全局共享上下文并将其放在 AppDelegate 中是可以的,但是 2.使用协议来避免将自己锁定在那个或任何其他特定的单例中。根据我的经验(在 ios 和 obj-c 之外),有时单个全局上下文最终会升级到多个上下文,需要大量重构 - 但您的解决方案使这变得容易。用于 VC 的 IBOutlets 也很不错 在这种情况下..你可以让一个单例类实现这个协议..然后所有的类都可以从那个单例中请求它(即而不是让应用程序委托实现它)..似乎有强烈的opinions 反对让应用程序代表像模型一样行事.. +1 是一个很好的答案 我还担心一个问题是,如果任何对象都可以实现此协议..那么您可以根据您要求的对象拥有不同的数据库源..这不会有潜在的混淆吗? 【参考方案2】:这是访问这些变量的正确方法还是我应该 做一些不同的事情吗?
你会发现不同的人对此有不同的看法。我更喜欢的风格是让应用程序委托将必要的信息传递给第一个视图控制器,然后让该视图控制器将其传递给它创建的任何视图控制器,依此类推。这样做的主要原因是它可以防止子视图控制器依赖于他们不知道的事情。
例如,如果您有一些详细信息编辑器,您希望能够准确地传递该编辑器完成其工作所需的内容。如果你给它那个信息,编辑器是完全灵活的——它会编辑你给它的任何信息。如果编辑器知道它应该从某个外部对象(例如应用程序委托)获取数据,那么它就会失去一定程度的灵活性——它只能从它知道的事物中获取数据。
因此,可以在应用委托中设置您的数据模型。但在提供对模型的访问权时,请考虑:告诉,不要问。也就是说,让应用程序委托告诉第一个视图控制器要使用什么模型对象,然后让该控制器告诉下一个,依此类推。如果你必须询问,你必须知道谁去询问,这就是依赖关系开始走向错误方向的地方。
每次我将 AppDelegate 导入 .m 文件以访问这些 变量的数据我觉得我做错了。
相信这种直觉。想想为什么感觉不对。
【讨论】:
+1 很好的方式来解释依赖建立的破坏性。 对于视图控制器中的模型通常是很好的建议,但有时会有关于应用程序的 状态 的全局上下文信息不在模型中,如果你在哪里它会变得不必要地冗长将它从 VC 传递到 VC 到 VC。对于这些情况,我发现 LavaSlider 对下面的回答是最优雅的解决方案。 我经常使用它来获取驻留在 Appdelegate 中的 managedObjectContext。我该怎么做才能避免这种情况?【参考方案3】:这是一种方法,是的,但它不是很优雅。单例也是一种方式,是的,但不是很优雅:) - 如果您必须模拟所有单例,那么测试您的代码并不容易。 相反,我可能会做的是为服务提供者设置一个单例,并向该服务提供者询问您的模型提供者的实例。
假设您的服务提供者类是一个单例,您需要访问模型以查看用户详细信息。我会按照以下方式进行:
JMUserDetailModel *myModel = [[[JMServiceProvider sharedInstance] modelProvider] userDetailModel];
这意味着您将创建一个 JMServiceProvider 类来注册服务,并能够检索这些服务。这些服务有点像单例,但是如果您需要对代码进行单元测试,那么注册一个与原始服务相同的不同服务只是小菜一碟。
希望这能回答你的问题。
编辑:阅读这篇文章:http://martinfowler.com/articles/injection.html - 一个很好的解释面向服务架构的文章......
【讨论】:
【参考方案4】:查看单身人士。它们可以成为一种更优雅的方式来管理全局数据。
【讨论】:
【参考方案5】:我也在导入它们并像这样使用它:
AppDelegate *delegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
[delegate variable];
另一种方法是使用单例。
【讨论】:
【参考方案6】:您应该认真避免在任何地方导入 AppDelegate,并且每次执行此操作时都会觉得自己做错了什么(为此 +1)。您实际上是在创建一个Big Ball of Mud,应该重新考虑您的设计。例如,如果您将 CoreData 用于模型,请考虑使用诸如 Magical Panda Active Record 之类的框架来检索数据。我在一个企业应用程序上工作,AppDelegate.h
仅包含在AppDelegate.m
中。
【讨论】:
【参考方案7】:将此方法放在您的 AppDelegate 类中
+ (AppDelegate *)get
return (AppDelegate *) [[UIApplication sharedApplication] delegate];
当你需要调用你的 AppDelegate 时:
[AppDelegate get];
【讨论】:
以上是关于导入 AppDelegate的主要内容,如果未能解决你的问题,请参考以下文章