【问题标题】:Global variables as aliases for singletons?全局变量作为单例的别名?
【发布时间】:2009-04-26 00:35:36
【问题描述】:

我正在开发一个 Cocoa (Touch) 应用程序,我必须在不同的视图和控制器之间保留某些数据(例如自己的设备信息和位置列表)。

我曾想过将它作为实例变量存储在我的 App Delegate 中,但对代理进行寻址非常麻烦(每次我想访问位置数组时都输入 [[[UIApplication sharedApplication] delegate] locations] 并不快乐,而且它有很多地方),所以我想为代表引入某种别名(a la NSApp),但除了 NSApp,我在其他 Cocoa 应用程序中没有经常看到这种情况。

我还想更进一步,为我的单例类引入别名,所以为什么不将 [State sharedState] 重命名为 _State 并为其创建一个名为 State 的单个实例?

【问题讨论】:

    标签: objective-c cocoa oop naming-conventions singleton


    【解决方案1】:

    我可能会编写一个像 LocationManager 这样的类,它提供一个带有 [LocationManager sharedManager] 或类似的单例。通过委托调用会破坏封装(并且您通过 3 个对象调用以到达那里)。即使是 NSApp 风格的#define 也不能解决这个问题。

    【讨论】:

    • 位置只是一个简单的井数组,Location 对象,所以引入LocationManager 会不会有点太多开销?我的意思是,我的电话将是[[LocationManager sharedManager] locations],这并不是更紧凑。也许是一个静态变量,它保存着可以通过[Location list][Location setList:] 访问的数组
    【解决方案2】:

    使用方法调用来获取单例的主要目的是使单例可以延迟准备。例如:

    static State sharedStateInstance;
    
    @implementation State
    + (id)sharedState {
        if (!sharedStateInstance)
            sharedStateInstance = /* Allocate instance */;
        return sharedStateInstance;
    }
    @end
    

    所以这意味着如果没有代码调用+sharedState,则不会花费任何资源来创建它。

    此外,如果将来出现其他需求,例如确保每个线程有一个实例,或所有线程共享一个实例(然后需要在初始化时锁定代码),可以在一个地方改进此代码。

    【讨论】:

      【解决方案3】:

      Cocoa 鼓励使用 [Foo sharedFoo] 而不是全局 Foo 对象有几个原因。

      [Foo sharedFoo] 可以在第一次使用时自动实例化。

      用大写字母命名实例非常令人困惑,因为它看起来像一个类,鼓励错误。命名的一致性是良好 Objective-C 的核心。由于 ObjC 是高度动态的,因此编译器无法保护您免受各种错误的影响。良好的命名和一致性的自律是导致 Cocoa 无错误的原因。

      并行度:

      Foo *foo = [Foo sharedFoo];
      Foo *foo = [[[Foo alloc] init] autorelease];
      Foo *foo = [Bar fooAtIndex:0];
      

      所有这三个可能在同一个程序中都是合法的。仅仅因为有一个单例实例并不意味着也没有其他实例。 NSNotificationCenter 就是一个很好的例子。这是一个单例,但您可以创建其他实例(并且有这样做的理由)。

      全局变量可以被全局修改。 sharedInstance 不能。例如,如果State 是一个全局变量(而不是一个类),那么State=nil 在程序中的任何位置都是合法的。这破坏了封装,并且对于state=nil 来说是一个容易被编译器捕获的错字。如果State 是一个类,那么编译器可以捕获这个简单的错误。

      Cocoa 命名的许多规则都是为了鼓励高度可读的代码,并在高度动态和松散类型的环境中最大限度地减少错误。就像 Perl 的 use strict 一样,在放弃我们仅有的一点安全网之前,我们应该非常小心。

      【讨论】:

      • 我同意你所说的,我只是想避免每次想访问代理时输入太多。这就是我使用#define 别名的原因
      • 如果您发现自己经常访问应用程序委托,您可能对它挂了太多。挂在应用程序委托上的许多东西应该传递给其他对象,或者应该是单例。也就是说,虽然我不喜欢#define(因为它使使用调试器变得更加困难),但只要你把事情命名好,它并不总是错误的。例如,我使用了#define AppDelegate [[UIApplication sharedApplication] delegate]。如果将其称为 SharedLocations,您的 FOO 定义可能没问题。也就是说,Cocoa 强烈鼓励易于阅读和理解,而不是简短的类型。
      【解决方案4】:
      #define FOO [[[UIApplication sharedApplication] delegate] locations] 
      

      【讨论】:

      • 是的,定义是个好主意。也许有演员表,以避免警告
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-04-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多