Objective-C:使用singleton vs. use类作为对象?

时间:2013-09-14 06:08:58

标签: objective-c singleton factory resourcemanager

我一直想知道在什么情况下在objective-C中采用单例模式是非常必要的(例如,定义一个专用类并创建一个单独的实例),使用类作为对象是不行的。

特别是,我正在考虑以下解决方案:

  1. 在单例实例上定义并使用适当的类方法,而不是实例方法;
  2. 使用static变量(文件范围全局变量),而不是单例实例的实例变量;
  3. 在注册为通知的观察者而不是单例实例时使用类对象。虽然类对象本身就是一个Objective-C对象(对吗?),但这需要注册的通知处理程序是一个类方法; (这可能吗?)
  4. 例如,您可以将所有纹理创建/清理实现为Texture类(模型对象) TextureManager单例(资源管理器),而不是类方法和相同Texture类的静态变量(工厂模式加上一些资源管理)。

    对此设计的任何想法?

    修改 现在我想到了它,并且仍然在上面的Texture示例中,即使我将两个类分开(TextureTextureManager),我必须在A之间做出选择。让经理成为单例,并使用实例方法操作它,或B.使管理器成为无实体的辅助类。澄清:

    Texture* myTexture = [[TextureManager defaultManager] textureWithName:@"TextureName"]; 
    // (singleton, client uses instance methods)
    

    Texture* myTexture = [TextureManager textureWithName:@"TextureName"]; 
    // (Class standing in for singleton, client uses class methods)
    

    后者看起来更简单,不那么繁琐/冗长,但我想知道哪种设计“更正确”。当然,前者允许出现不止一个TextureManager实例(不是在我的情况下)。

3 个答案:

答案 0 :(得分:7)

我一直在考虑同样的事情,我想我有一个答案。

这取决于你需要做什么。两者都不一定更“正确”。

如果您想详细了解我的结论或向下滚动到 tl; dr 部分,请继续阅读。

正如你所说,访问单例以让类为你管理单例会显得(外部)不那么麻烦。基本上你可以通过用初始化方法替换singleton的工厂方法来实现。查看Apple's documentation,您可以看到它们显示“共享”方法的位置,该方法充当工厂,根据需要生成单例。

static MyGizmoClass *sharedGizmoManager = nil;

+ (MyGizmoClass*)sharedManager
{
    if (sharedGizmoManager == nil) {
        sharedGizmoManager = [[super allocWithZone:NULL] init];
    }
    return sharedGizmoManager;
}

您可以使用void初始值设定项替换方法,而不是这样:

+ (void)initializeMyGizmo
{
    if (sharedGizmoManager == nil) {
        sharedGizmoManager = [[super allocWithZone:NULL] init];
    }
    // whatever else needs to be done to the singleton to initialize it
}

然后使用类方法,并允许MyGizmoClass管理单身内容的更新,例如[MyGizmoClass setGizmoName:@"Gadget"]

注意:方案中,对于查看.h文件以查看属性的人来说会很困惑,在这种情况下,他们可能会得出结论他们应该自己创建一个对象的实例,或者能够以某种形式或方式访问单例。所以如果你想要封装对单例的访问权限,那么使用公共变量是不明智的。

到那时:

如果您仅通过课程本身限制访问权限,则会丢失任何与属性一起出现的getter和setter或其他免费内容。这意味着,如果MyGizmoClass作为其模型的一部分NSString *gizmoName,您将被迫为此“属性”创建自定义getter和setter,并将其保存为接口中的ivar或property单例类的.m文件(即私有)中的扩展,或者作为相邻的静态变量。

所以这引出了一个问题(并且首先让我思考的是),我们是否应该包含行static MyGizmoClass *sharedGizmoManager = nil;或者我们是否可以完全修改内部接口扩展并替换任何可能的ivars或属性我们想要在实现中限制对static实现的访问?

我已经回答了......

  

这取决于您需要做什么。

TL;博士

第一个场景

如果你(甚至一点点机会)需要继承你的 TextureManager或者可以创建它的多个实例(制作它 不再是单身人士)坚持常规会更好 单身的Apple惯例。

这包括多个“单身人士”,其中你可能有几个 TextureManager已预先配置了不同的设置。

在这种情况下,您可以根据需要使用属性(公开或 私人)以及伊娃。你也可以混合使用ivars和 静态,但你仍然需要有一个静态实例 TextureManager实施中的TextureManager

第二个场景

如果您将需要一个 TextureManager的实例,它将完全独立运行而不会进一步向下混合然后,您可以在.m文件的实现中完全删除类的静态实例,并将ivars和properties替换为该实现中的静态变量。

如果要在CoreData中存储属性或设置,并且只需要它们进行配置,这将非常有用。

请记住在这种情况下,您必须为静态变量创建所有getter和setter,并且只能使用类方法访问它们(但这很重要)。

其他有趣的东西

This answer为何时以及如何调用“初始化程序”方法或创建单例提供了一个有趣的解决方案。这可以与每个场景一起使用,以初始化第一个场景中的单例,或者将默认值预加载到第二个场景中的类级静态。

如果您想在实现中坚持使用静态单例,您可以查看this article,以便更好地了解单例的真正“全局范围”。

答案 1 :(得分:2)

是的,你绝对可以制作一个Texture类而不需要单例。

单身人士可能不应该被创造并用作对象。

单身人士可以用于许多重要的事情 我当然不知道他们可以用的所有东西,但我会告诉你我过去用过的东西。

我通常在多级游戏(如愤怒的小鸟)中使用单身人士进行关卡导航 通过关卡导航,我的意思是......当一个玩家在游戏中完成某个关卡时,我只需在单例上调用一个类方法并传入关卡编号,然后单例的类方法计算下一个关卡(如果用户按下) '下一级'按钮)。

答案 2 :(得分:2)

我可以帮助您更好地理解Singleton类,并在适用时。


模式:Singleton

意图:强制某个类只能拥有一个实例,并使该实例可以被任何其他对象访问。

动机:有时我们需要确保在我们的问题域中只存在某种类型的单个对象。示例:学生只携带一个背包,可以装满书籍。我们不想把他和二手背包联系在一起,甚至还有更多的书。

何时使用

  • 只需要一个类的单个实例,并且该实例必须可以从代码中的不同对象访问。
  • 当您(可能)需要通过子类化为该类添加更多功能时。