2013-06-28 17 views
13

Я реализовал объект singleton с использованием обычного шаблона. Мой вопрос: возможно ли вернуть этот объект в нуль, так что на более поздней странице [MySingleton sharedInstance] объект будет повторно инициализирован?Можно ли установить singleton обратно в ноль?

// Get the shared instance and create it if necessary. 
+ (MySingleton *)sharedInstance { 

    static dispatch_once_t pred; 
    static MySingleton *shared = nil; 
    dispatch_once(&pred, ^{ 
     shared = [[MySingleton alloc] init]; 
    }); 
    return shared; 
} 

// We can still have a regular init method, that will get called the first time the  Singleton is used. 
- (id)init 
{ 
    self = [super init]; 

    if (self) { 
    // Work your initialising magic here as you normally would 

    } 
    return self; 
} 

Я думаю, что

MySingleton *shared = [MySingleton sharedInstance]; 
shared = nil; 

только задает локальный указатель shared на nil. В конце концов, shared объявлен как static.

ответ

38

Ваше предположение о местной ссылке верно, это не повлияет на ваш синглтон.

Чтобы иметь возможность повторно инициализировать синглтон, вам нужно переместить статическую переменную из вашего метода, чтобы она была доступна всему классу.

static MySingleton *sharedInstance = nil; 
// Get the shared instance and create it if necessary. 
+ (MySingleton *)sharedInstance { 
    if (sharedInstance == nil) { 
     sharedInstance = [[MySingleton alloc] init]; 
    } 
    return sharedInstance; 
} 

+ (void)resetSharedInstance { 
    sharedInstance = nil; 
} 

Обратите внимание, что вы не можете использовать dispatch_once больше, так как ваш синглтон нужно, очевидно, будет создано несколько раз. Если вы только когда-либо называете этот синглтон из своего пользовательского интерфейса (и, следовательно, только из основного потока), то пример выше в порядке.

Если вам нужен доступ из нескольких потоков, вам необходимо установить блокировку по методу +sharedInstance и +resetSharedInstance, например.

+ (id)sharedInstance { 
    @synchronized(self) { 
     if (sharedInstance == nil) { 
      sharedInstance = [[MySingleton alloc] init]; 
     } 
     return sharedInstance; 
    } 
} 

+ (void)resetSharedInstance { 
    @synchronized(self) { 
     sharedInstance = nil; 
    } 
} 

Это совсем немного медленнее, чем dispatch_once вариант, но на практике это не будет иметь значения, как правило.

+0

Как я и предполагал. Спасибо за хороший ответ. – SkeetSkeet

4

Да, но sharedInstance метод вашего Singleton определяет его как static внутри этого метода, и ваш последний пример кода только настройки локальной переменной (по совпадению также называется shared) для nil, оставляя static внутри sharedInstance в неизмененном виде. Таким образом, вы просто nil, указав местный указатель, не меняя static внутри sharedInstance.

Если вы хотите, чтобы делать то, что вы просите, вы должны будете тянуть переменную static, shared, из sharedInstance метода (и, вероятно, написать некоторый reset метод nil его). Ваш метод sharedInstance также больше не может полагаться на dispatch_once, но скорее должен проверить, не является ли это staticnil или нет.

3

Я сделал это. Я не уверен, что это лучший способ, но, похоже, он работает нормально.

static dispatch_once_t pred; 
static MySingleton *shared = nil; 

+(MySingleton *)sharedInstance { 
     dispatch_once(&pred, ^{ 
      shared = [[MySingleton alloc] init]; 
     }); 
     return shared; 
    } 

+(void)clearSharedInstance { 

     shared = nil; 
     pred = nil; 
} 
+0

Не следует сбрасывать предикат dispatch_once. См. Здесь для получения дополнительной информации: http://stackoverflow.com/questions/10930044/possible-to-reset-state-of-dispatch-once-in-unit-test-to-make-them-run-again – Alfonso

+0

@ Fönsi Кажется, я должен установить значение 0 вместо нуля. Верный? – Hackmodford

+1

Да и нет. Вы правы в том, что 'dispatch_once_t' является' long', и поэтому вы должны использовать '0' вместо' nil'. Но большая проблема, которую я хотел подчеркнуть, состоит в том, что концепция «перезагрузки» отправки после предиката опасна. – Alfonso