关于objective c:带参数iOS的Singleton

Singleton with parameter iOS

我需要实现一个接受参数的单例类。同一个对象将每次作为一个参数传递,因此生成的单例对象将始终相同。

我在做下面的代码。这个看起来可以吗?有没有更好的方法来实现我想要实现的目标?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
  - (id)sharedInstanceWithAccount:(UserAccount *)userAccount {
      if (!sharedInstance) {
        @synchronized(self) {
          sharedInstance = [[[self class] alloc] initWithAccount:userAccount];
        }
      }

      return sharedInstance;
    }

    - (id)initWithAccount:(UserAccount *)userAccount {
      self = [super init];
      if (self) {
        _userAccount = userAccount;
      }

      return self;
    }

    - (id)init {
      NSAssert(false,
               @"You cannot init this class directly. It needs UserAccountDataSource as a paramter");
      return nil;
    }

    + (id)alloc {
      @synchronized(self) {
        NSAssert(sharedInstance == nil, @"Attempted to allocated a second instance of the singleton");
        sharedInstance = [super alloc];
        return sharedInstance;
      }
      return nil;
    }


此设计中存在许多问题:

  • 按照苹果公司的建议,如果单台机器的dispatch_once而不是@synchronized(self)

    1
    2
    3
    4
    5
    6
    7
    static MyClass *sharedInstance = nil;
    static dispatch_once_t onceToken = 0;
    dispatch_once(&onceToken, ^{
         sharedInstance = [[MyClass alloc] init];
         // Do any other initialisation stuff here
    });
    return sharedInstance;

    请参考这个问题了解更多细节:为什么苹果建议在arc下使用dispatch_一次来实现singleton模式?

  • 坏的API设计把单体放在alloc中。

    如方法alloc的名称所示,这意味着将分配一些内存。然而,在你的情况下,事实并非如此。这种覆盖alloc的尝试会给团队中的其他程序员带来混乱。

  • 在你的-init中使用NSAssert是个坏主意。

    如果要禁用某个方法,请通过将其放入头文件来禁用该方法:

    1
    - (id)init __attribute__((unavailable));

    在这种情况下,您将得到一个编译错误,而不是在运行时崩溃应用程序。请参阅本文了解更多详细信息:覆盖核心数据属性的方法:isDeleted

    此外,您甚至可以添加不可用的消息:

    1
    - (id)init __attribute__((unavailable("You cannot init this class directly. It needs UserAccountDataSource as a parameter")));
  • 有时会忽略输入参数而不发出警告。

    在下面的代码中,调用此函数的程序员如何知道,如果某个类的实例已经由其他人创建,则有时会忽略输入参数userAccount

    1
    2
    3
    4
    5
    6
    7
    8
    - (id)sharedInstanceWithAccount:(UserAccount *)userAccount {
        if (!sharedInstance) {
            @synchronized(self) {
               sharedInstance = [[[self class] alloc] initWithAccount:userAccount];
            }
        }
        return sharedInstance;
    }
  • 简而言之,不要认为用参数创建单例是个好主意。使用传统的单件设计更清洁。


    1
    2
    objA = [Object sharedInstanceWithAccount:A];
    objB = [Object sharedInstanceWithAccount:B];

    B被忽略。objb中的用户帐户是A。

    如果objb中有useraccount b,您将更改sharedInstanceWithAccount。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    - (id)sharedInstanceWithAccount:(UserAccount *)userAccount {
        static NSMutableDictionary *instanceByAccount = [[NSMutableDictionary alloc] init];
        id instance = instanceByAccount[userAccount];

        if (!instance) {
            @synchronized(self) {
                instance = [[[self class] alloc] initWithAccount:userAccount];
                instanceByAccount[userAccount] = instance;
            }
        }

        return instance;
    }