iPhone 应用程序委托的正确使用方法

58

我希望能够在我的应用程序中通过任何位置引用特定的状态/对象。例如,用户登录到他们的应用程序,我需要调用 Web 服务并检索用户信息。然后我想能够使用以下内容从应用程序中的任何地方访问此信息:

myAppDelegate *delegate = [[UIApplication sharedApplication] delegate];
user = delegate.u;

将实例变量设置为应用程序委托中的用户对象,并在需要时从那里引用它,这种方法是否不太好?我通常在用户登录时在那里进行设置。

想听听专业人士如何处理这个问题。

4个回答

52

通常情况下,您只应该将东西连接到应用程序委托(delegate)上,如果它们符合以下条件:

  • 与应用程序委托从同一个NIB文件中创建(i.e.单窗口界面中的静态UI元素)
  • 与通过应用程序委托处理的应用级事件相关联(如偏好设置窗口的菜单项)

对于其他所有内容,您应该创建一个管理访问它们的单例。

Jason Coco建议通过应用程序控制器(Application Controller)进行路由。在我的程序中,我通常避免这样做,因为我认为它会将太多的责任放在顶层——我认为应该尽可能自我管理,而只有在需要协调同级模块之间时才应使用更高层次的管理。

我不打算链接自己的博客,但如果您在Google上搜索我的名字和单例,您可能会找到我写的一篇更详细的文章。


6
好的,请提供需要翻译的内容。 - Casebash
5
我对在Cocoa代码中普遍使用单例模式的想法缺乏批评而感到失望。单例模式在某种程度上易于理解和“干净”(没有以应用程序委托形式存在的全局神类),但它们并不适合很好地进行测试。大多数经验丰富的TDD实践者会建议使用DI,可以使用构造函数参数来传递状态或状态对象。我正在寻找有关如何在Cocoa中最好地实现此目标的信息,但我发现的所有建议都是让我们使用单例模式。 - jkp
8
单例模式在进行单元测试时可能会比较棘手,但是不要把这个理解为单例模式本身不好。事实上恰恰相反:单元测试只擅长命令模式的实现,对于每个单元测试设置状态机(像常见的桌面应用程序模型)非常麻烦,这是单元测试的缺陷。仅仅为了避免使用单例而为应用程序状态的每个元素使用方法参数是一个丑陋的设计模式。正确的解决方案是在调用测试之前像处理其他对象一样交换 mock 单例(更改 sharedInstance 方法返回的对象)。 - Matt Gallagher
1
@jkp:所有单例类都有一个返回单例的方法(即+sharedInstance)。你所需要做的就是有一个单独的+setSharedInstance方法(仅由单元测试使用),在单元测试的设置中调用它以交换模拟对象。如果您计划并行运行此代码,则需要在+setSharedInstance中获取锁,该锁在将共享实例返回其原始值时释放。 - Matt Gallagher
1
呃...在set中获取锁并在访问器中释放它?听起来像是一场灾难的配方。此外,大多数单例实现都使用本地静态作用域限定在单例访问器本身,因此如果不改变该实现,就无法做到你所说的事情。在我看来,这些都不是很“干净”,但我理解你的观点,这是一种方法。 - jkp
显示剩余3条评论

31

13

我认为你的方法没有问题。我通常使用单例来处理这种情况:

// MyCommon.h:
@interface MyCommon
class MyCommon : NSObject
{
    int user;
};

@property(assign) int user;

+ (MyCommon *)singleton;

@end

// MyCommon.m:
@implementation MyCommon

static MyCommon * MyCommon_Singleton = nil;

+ (MyCommon *)singleton
{
    if (nil == MyCommon_Singleton)
    {
        MyCommon_Singleton = [[MyCommon_Singleton alloc] init];
    }

    return MyCommon_Singleton;
}
@end

接下来,MyCommon单例将在应用程序的任何地方使用,如下所示:

int user = [MyCommon singleton].user;

1
我几年前发布过这篇文章,那时候我学到了一些东西。关于单例模式,重要的是要认识到它们与全局变量没有什么不同。这并不意味着在所有情况下都应该避免使用它们,只是意味着它们具有相同的缺点,特别是在正确封装和易于测试方面。例如,在这种情况下,如果没有先设置全局user值,就很难单独测试依赖于[MyCommon singleton]的各个类。 - e.James
一个很好的替代全局可访问的单例模式的方法是使用依赖注入。你可以在应用程序委托中创建相同的 MyCommon 对象,然后将其传递给任何需要它的子对象,并继续这个过程直到你的对象层次结构的最底层。这样做会增加一些额外的工作量,但它会产生一个更面向对象的程序,易于维护和调试。 - e.James
为了了解SO社区对此问题的看法,这里是我在开始转换时提出的一个关于单例模式的问题链接:https://dev59.com/2HRB5IYBdhLWcg3w6LN2 - e.James

3
通常情况下,您会向应用程序的控制器询问此信息,并负责知道如何在任何数据模型中存储它/查找它。您的应用程序控制器可能与应用程序委托相同(在大多数简单应用程序中,它是相同的)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接