0

我在我的应用程序中实现了一个单例,但是当我在视图控制器之间切换时遇到了问题。

我的应用程序从一个视图控制器 MainMenu 开始,然后在选择菜单时切换到 Game 视图控制器。我在 Game VC 中有一个单例类,它是游戏的对象管理器(称为 World)。它根据 MainMenu 中的菜单选择加载内容。我可以加载并退出 MainMenu VC。单身人士工作正常。当我再次从 MainMenu 中选择(从 MainMenu VC 到 Game VC)时,我的应用程序崩溃导致 NSAssert 阻止单例 World 再次分配,这是有意的。

如何在不尝试重新初始化我的单例的情况下切换回 Game VC?

本质上,我想跳过[[world alloc] init]我的 Game VC 的 init 方法中的行。我无法弄清楚如何正确地做到这一点......它需要能够在第一次(当 World 不存在时)或任何后续时间(当 World 已经作为单例存在时)被处理。我已经尝试过if (!world)了,但这不起作用。

这也引出了一个问题……我是否在正确的地方实现了我的单例?我应该把它放在 MainMenu 中吗?我只是想避免当我在两个 VC 之间切换时尝试重新初始化/重新分配单例。

如果这有帮助,我将通过使用[self.view removeFromSuperview]; 是否应该以不同的方式退出 Game VC?

4

2 回答 2

2

如果我正确理解了这个问题,您想知道如何创建单例类吗?最近比较常用的方式是使用grand-central-dispatch,如下:

+ (MyViewController*)shared
{
    static dispatch_once_t onceToken;
    static MyViewController* instance;

    dispatch_once(&onceToken, ^
    {
        instance = [[[self class] alloc] init];
    });
    return instance;
}

然后,您可以使用以下方式访问实例:

[MyViewController shared];

在 grand-central-dispatch 之前,技术是在类上放置一个静态实例,并在调用共享访问器时检查它是否为 nil。并且访问器需要用 @synchronized(self) 包装以确保线程安全。GCD 方式已被证明表现得更好一些。

话虽如此,我建议您使用带有意志的单例。对为什么它们被视为反模式进行一些研究,并研究依赖注入作为替代方案。

依赖注入与单例

单例的问题在于它们会导致紧密耦合。假设您正在构建一个航空公司预订系统。您的预订控制器可能会使用:

id<FlightsClient>

在控制器中获取它的常用方法如下:

_flightsClient = [FlightsClient sharedInstance];

为了使用单例测试一个类,您现在必须同时测试单例,这可能会变得很棘手,尤其是当您的应用程序变得更加复杂时。想象一下测试 A 类,依赖于 B 类,依赖于 C 类,依赖于.... 没什么好玩的!

另一种方法是使用依赖注入,这意味着使用初始化程序或属性设置器将预订客户端传递给预订控制器。您可以手动执行此操作,也可以使用库。如果您手动执行它仍然可以声明单例,尽管可以说您不需要,因为顶级程序集类可以保留实例。

通过对单例使用这种替代方法,类将具有松散耦合,并且易于测试和维护。(并且使用依赖注入,您将拥有一个同样花哨的名称;))

我写了一个依赖注入库,基于 Spring:https ://github.com/jasperblues/spring-objective-c

于 2013-01-15T06:49:45.720 回答
1

正如我所想的那样,我确实可以通过将单例初始化向上移动到 MainMenu 视图控制器来解决这个问题。在我的设计中,init该 VC 仅在应用程序启动时调用一次,而init我的 Game VC 中的自定义方法在我每次切换到它时都会被调用。

不确定这是否是我应该使用的最佳设计,但它现在有效。

于 2013-01-15T08:33:48.970 回答