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

Nic*_*ari 14 singleton factory objective-c resourcemanager

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

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

  1. 在单例实例上定义和使用适当的类方法,而不是实例方法;
  2. 使用static变量(文件范围全局变量),而不是单例实例的实例变量;
  3. 在注册为通知的观察者而不是单例实例时使用类对象.虽然类对象本身就是一个Objective-C对象(对吗?),但这需要注册的通知处理程序是一个类方法; (这可能吗?)

例如,而不是一个Texture类(模型对象)一个TextureManager单(资源管理器),你可以有作为类的方法和相同的静态变量实现的所有纹理制作/清除Texture类(工厂模式加上一些资源管理).

有关这个设计的任何想法?

编辑: 现在我想到了它,仍然在Texture上面的例子中,即使我将这两个类分开(TextureTextureManager)我必须在A之间做出选择.让经理成为单身人士,并使用实例方法或B.让经理成为无实体的辅助类.澄清:

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

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

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

Dea*_*lly 7

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

这取决于你需要做什么.两者都不一定更"正确".

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

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

static MyGizmoClass *sharedGizmoManager = nil;

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

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

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

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

注意:这种情况下,查看.h文件以查看属性会让人感到困惑,在这种情况下,他们可能会得出结论,他们应该自己创建一个对象的实例,或者能够访问一些单例.形式或时尚.因此,如果您要使用封装访问单例的路径,那么使用公共变量是不明智的.

到那时:

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

因此,这引出了一个问题(是什么让我在第一时间琢磨),我们甚至应该包括行static MyGizmoClass *sharedGizmoManager = nil;所有,或者我们可以完全尼克斯内部的接口扩展和替换,我们要限制对任何可能的ivars或属性static实现中的实现?

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

这取决于你需要做什么.

TL;博士

第一种情景

如果你曾经(甚至是最轻微的机会)需要子类化你的 TextureManager或者可以创建它的多个实例(使它不再是单例),那么最好坚持常规的单一Apple规则.

这包括多个"单身人士",其中您可能有几个 TextureManager预先配置了不同的设置.

在这种情况下,您可以根据需要使用属性(公开或私有)以及ivars.你也可以使用实例变量和静态的组合,但你还是会一直需要有你的静态实例TextureManager的内部TextureManager执行.

第二种情景

如果你ONLY永远都需要ONE的实例 TextureManager,它会运行完全没有进一步混在向下行,那么你可以在内部实现中完全删除你的类的静态实例独立位置.m的文件,并与内的静态变量代替实例变量和属性实现.

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

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

其他有趣的东西

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

如果您想在实现中坚持使用静态单例,您可以查看本文,以便更好地了解单例的真正"全局范围".