我经常在AppDelegate中初始化我的模型类变量,因此不同的ViewControllers可以使用它们,而不会跨类文件传递它们的实例。但是,每次我将AppDelegate导入到.m文件中以访问这些变量的数据时,我觉得我做错了。
这是访问这些变量的正确方法,还是我应该采取不同的方式?
修改 我的问题不是如何访问变量。我目前使用这行代码来获取我的appDelegate实例:
id appDelegate = [[UIApplication sharedApplication] delegate];
从概念上讲,我想知道这是否是与应用程序模型类交互的可接受方式。对我来说,似乎应用程序的AppDelegate整体管理应用程序。因此,将此类导入到应用程序类链中的其他类中似乎违反直觉。
答案 0 :(得分:15)
这是访问这些变量的正确方法,还是我应该这样做 做不同的事情?
你会发现不同的人对此有不同的看法。我喜欢的样式是让app delegate将必要的信息传递给第一个视图控制器,并让视图控制器将它传递给它创建的任何视图控制器,依此类推。这样做的主要原因是它可以防止子视图控制器依赖于他们没有业务知道的事情。
例如,如果您有一些详细信息编辑器,您希望能够准确地传递该编辑器完成其工作所需的内容。如果您提供该信息,编辑器是完全灵活的 - 它将编辑您提供的任何信息。如果编辑器知道它应该从某个外部对象(如应用程序委托)获取其数据,那么它会失去一定程度的灵活性 - 它只能 从它所知道的事物中获取数据。 / p>
因此,可以在app委托中设置数据模型。但是在提供对模型的访问时,请考虑:告诉,不要问。也就是说,让app委托告诉第一个视图控制器使用什么模型对象,让控制器告诉下一个,依此类推。如果您必须询问,您必须知道谁要求,并且这是依赖关系开始朝错误方向前进的地方。
每次我将AppDelegate导入.m文件以访问它们 变量的数据我觉得我做错了。
相信本能。想想为什么感觉不对。
答案 1 :(得分:9)
我同意,有时似乎AppDelegate
是放置您只需要实现一次但可能需要从几个地方实现的东西的合理位置。如果这些东西很复杂,那么为每个单独创建一个单例是很好的,但它确实会为项目创建大量额外的文件和混淆。我也同意这里的大多数答案,在AppDelegate
上建立依赖关系是一个非常糟糕的设计。
我认为最好的解决方案是创建协议!然后将IBOutlet
放到属性中,以执行您需要在每个需要该函数的控制器中完成的操作。 协议是解开类的标准目标-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
1}}万一我懒惰或健忘,不做上述任何一种情况。在他们的.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;
}
答案 2 :(得分:3)
你应该认真避免在任何地方导入AppDelegate,你应该觉得每次做错都会出错(+1为此)。您实际上是在创建Big Ball of Mud并且应该重新考虑您的设计。例如,如果您正在为模型使用CoreData,请考虑使用Magical Panda Active Record等框架来检索数据。我处理的是企业应用程序,AppDelegate.h
仅包含在AppDelegate.m
中。
答案 3 :(得分:2)
我也正在导入它们并像这样使用它:
AppDelegate *delegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
[delegate variable];
另一种方法是使用Singleton。
答案 4 :(得分:1)
将此方法放在AppDelegate类
中+ (AppDelegate *)get {
return (AppDelegate *) [[UIApplication sharedApplication] delegate];
}
当您需要使用AppDelegate时:
[AppDelegate get];
答案 5 :(得分:1)
这是做到这一点的一种方式,是的,但它不是很优雅。单身人士也是一种方式,是的,但不是很优雅:) - 并且如果你不得不嘲笑你所有的单身人士,那就不容易测试你的代码。 相反,我可能会做的是为服务提供商提供一个单例,并向该服务提供商询问您的模型提供者的实例。
假设您的服务提供者类是单例,您需要访问该模型以获取查看用户详细信息。我会这样做:
JMUserDetailModel *myModel = [[[JMServiceProvider sharedInstance] modelProvider] userDetailModel];
这意味着您将创建一个JMServiceProvider类来注册服务,并能够检索这些服务。这些服务有点像单身人士,但是如果你需要对你的代码进行单元测试,那么注册一个与原始服务行为相同的不同服务只是小菜一碟。
希望这能回答你的问题。
编辑:阅读这篇文章:http://martinfowler.com/articles/injection.html - 一个非常好的解释面向服务的架构......
答案 6 :(得分:0)
了解单身人士。它们可以成为管理全球数据的更优雅方式。