导入AppDelegate

通常我在我的AppDelegate中初始化我的模型类variables,以便它们可以被不同的ViewController使用,而不需要通过类文件传递它们的实例。 但是,每次我把AppDelegate导入到一个.m文件来访问这些variables的数据时,我觉得我正在做一些错误。

这是访问这些variables的正确方法,还是应该做一些不同的事情?

编辑:我的问题不是如何访问variables。 我目前使用这行代码来得到我的appDelegate实例:

id appDelegate = [[UIApplication sharedApplication] delegate];

从概念上讲,我想知道这是否是与应用程序的模型类交互的可接受的方式。 在我看来,应用程序的AppDelegate总体上pipe理着应用程序。 所以将这个类导入到应用程序的类链中的其他类似乎是违反直觉的。

这是访问这些variables的正确方法,还是应该做一些不同的事情?

你会发现不同的人对此有不同的看法。 我更喜欢的样式是让应用程序委托将必要的信息传递给第一个视图控制器,并将该视图控制器传递给它创build的任何视图控制器,依此类推。 这样做的主要原因是它阻止了子视图控制器依赖于他们没有业务知道的事情。

例如,如果您有一些细节编辑器,那么您希望能够正确地传递该编辑器所需的工作。 如果您提供这些信息,那么编辑器是完全灵活的 – 它会编辑您提供的任何信息。 如果编辑知道它应该从一些外部对象(如应用程序委托)获取数据,那么它就失去了一定程度的灵活性 – 它只能从它所知道的事物中获取数据。

因此,在应用程序委托中设置数据模型是很好的。 但是当提供访问模型时,请考虑: 告诉,不要问 。 也就是说,应用程序委托告诉第一个视图控制器使用什么模型对象,并让该控制器告诉下一个,等等。 如果你不得不 ,你必须知道问谁,这就是依赖关系开始朝着错误的方向前进的地方。

每次我把AppDelegate导入到一个.m文件来访问这些variables的数据时,我觉得自己做错了。

相信这种本能。 想想为什么感觉错了。

我同意,有时候, AppDelegate似乎是一个合理的地方,你只需要实现一次,但可能需要从几个地方。 为每一个创build一个singleton是好的,如果这些东西是复杂的,但它确实创造了很多额外的文件和项目混淆。 我也同意大部分答案, AppDelegate上build立依赖关系是一个非常糟糕的devise

我认为最好的解决scheme是创build一个协议 ! 然后将一个IBOutlet放到一个属性中,以完成您需要在每个需要该function的控制器中完成的操作。 协议是解耦类的标准目标C方法。

所以,作为一个例子,也许我有一个数据库的URL,我可能需要从一堆地方。 可能最好的办法是设置一个财产与它的每一步。 但在某些情况下,由于使用库存控制器而不想将其子类化,可能会很麻烦。 这是我的解决scheme:

创build文件:MainDatabasePovider.h

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

现在说任何实现MainDatabaseProvder协议的“任何人”(即任何类)都保证提供上述两种方法。 这可能是AppDelegateANY对象。

现在,如果我想要我的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可以是ANY对象。 我有能力在InterfaceBuilder或我的StoryBoard设置它(虽然我不真的认为这是一个用户界面项目,所以我通常不会这样做,但这是非常典型的),我可以设置它在我的控制器之外的代码之前它被加载在prepareForSegue:sender:tableView:didSelectRowAtIndexPath: 如果我没有设置它,它将默认为AppDelegate如果我已经正确configuration它。

当我忘记做晚年的事情时,我甚至可以安排一些保安人员,通过改变上面列出的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; } 

你应该认真地避免在任何地方导入AppDelegate,你应该觉得每次你做错了事(+1)。 你基本上是在创造一个大泥球,应该重新考虑你的devise。 例如,如果您正在为您的模型使用CoreData,则可以考虑使用“ 魔法pandas活动logging”等框架来检索数据。 我在一个企业应用程序上工作, AppDelegate.h只包含在AppDelegate.m

我正在导入他们,并使用它像这样:

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

另一种方法可以是使用单例。

把这个方法放在你的AppDelegate类中

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

而当你需要调用你的AppDelegate时使用:

 [AppDelegate get]; 

这是做的一种方式,是的,但是它不是很优雅。 单身也是一种方式,是的,但不是很优雅:) – 如果你不得不嘲笑你所有的单身人士,并且不容易testing你的代码。 相反,我可能会做的是有一个单一的服务提供商,并要求这个服务提供商为您的模型提供商的实例。

说,你的服务提供者类是一个单身人士,你需要访问该模型的查看用户的详细信息。 我会这样做:

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

这意味着您将创build一个JMServiceProvider类来注册服务,并能够检索这些服务。 这些服务有点像单身人士,但是如果你需要unit testing你的代码,那么注册一个与原始服务相同的服务就是一个小菜了。

希望这回答你的问题。

编辑:并阅读这篇文章: http : //martinfowler.com/articles/injection.html – 一个很好的解释面向服务的体系结构以及…

看单身。 他们可以成为pipe理全球数据的更优雅的方式。