2010-02-19 3 views
6

Вот что я придумал, после того, как поглядел на лингвистическую литературу. Я что-то забыл?Является ли это окончательной ref подсчитана реализация Objective C singleton?

@implementation MySingleton 

static MySingleton *mySharedInstance = nil; 

//called by atexit on exit, to ensure all resources are freed properly (not just memory) 
static void singleton_remover() 
{ 
    //free resources here 
} 

+ (MySingleton*) sharedInstance{ 
    return mySharedInstance; 
} 

+ (void)initialize { 
    if (self == [MySingleton class]) { 
     mySharedInstance = [[super allocWithZone:NULL] init]; 
    atexit(singleton_remover);  
    } 
} 

+ (id)allocWithZone:(NSZone *)zone 
{ 
    return [self sharedInstance]; 
} 

- (id)copyWithZone:(NSZone *)zone 
{ 
    return self;  
} 

- (id)retain 
{ 
    return self;  
} 

- (NSUInteger)retainCount 
{ 
    return NSUIntegerMax; //denotes an object that cannot be released 
} 

- (void)release 
{ 
    //do nothing  
} 

- (id)autorelease 
{ 
    return self;  
} 
+1

Пожалуйста, не изменяйте свой вопрос таким образом, чтобы существующие ответы были недействительными или больше не релевантными. – dreamlax

+0

Почему еще одна цепочка Objective-C singleton? См. Http://stackoverflow.com/questions/145154 для примера. – zoul

+0

Не могли бы вы просто использовать Gallagher's. Http://projectswithlove.com/projects/SynthesizeSingleton.h.zip от http://cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html. ..? – Fattie

ответ

2

, который позволяет избежать блокировки Синхронизировать наиболее времени

Если вы хотите, чтобы ваше программное обеспечение, чтобы быть надежным, избегать конструкций, которые работают «большую часть времени»

http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html

Таблица 4.1. Двойной контроль блокировки

Двойной контроль блокировки - попытка уменьшить накладные расходы при блокировке путем проверки критериев блокировки до фиксации. Поскольку блокировки с двойным проверкой потенциально опасны, система не предоставляет явной поддержки для них, и их использование не рекомендуется.

+0

-1 для неуместности и пламени. –

+0

Я думаю, что он думал «экономит процессор и синхронизацию большую часть времени». Тем не менее, с современными процессорами и оптимизаторами вполне может быть «правильная вещь в большинстве случаев». Но я признаю, что примечание Windows не нужно. – Darron

+0

Но проверенная блокировка _is_ ненадежна, или я не прав? * редактирование: * Я прав. http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html – glebm

1

Несколько предложений (больше для Mac какао, чем iPhone, но это может быть полезным для других людей, которые ищут в Objective-C тег):

  • Не связывайтесь с -allocWithZone: NULL, просто plain -alloc будет хорошо.
  • Рассмотрите возможность использования dispatch_once() или pthread_once(), где доступны
  • Использование atexit является умным, но не может быть совместима с прекращением быстрого приложения (не уверен, если это применимо на iPhone), так что эффективно убивает -9s приложение

Еще один шаблон весело:

+ (Foo *)sharedFoo { 
    static Foo *sharedInstance = NULL; 
    if (!sharedInstance) { 
     Foo *temp = [[Foo alloc] init]; //NOTE: This MUST NOT have side effects for it to be threadsafe 
     if (!OSAtomicCompareAndSwapPtrBarrier(NULL, temp, &sharedInstance)) { 
      [temp release]; 
     } 
    } 
    return sharedInstance; 
} 
+0

- использование alloc вызовет мой allocWithZone override, который получит nil на sharedInstance, так что это будет проблемой – Jacko

+0

Ах, конечно. Игнорируйте этот бит тогда :) –

0

singleton_remover функция не будет ничего делать, потому что вы переопределены release ничего не делать. Ваш метод allocWithZone: также выполняет аналогичный no-op, когда он отправляет retain в общий экземпляр (и полностью игнорирует выделение в указанной зоне). Возможно, у вас должен быть флаг, который переключает, является ли ваш общий экземпляр непобедимым (т. Е. Невозможен) или нет.

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

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

[[NSNotificationCenter defaultCenter] addObserver:self 
             selector:@selector(performCleanup:) 
              name:UIApplicationWillTerminateNotification 
              object:nil]; 
+0

спасибо. Что случилось с выпуском ресурсов в методе singleton_remover? – Jacko

+0

Ответом на 'NS/UIApplicationWillTerminateNotification' является документированный способ изящного управления ресурсами при завершении работы приложения. – dreamlax

+0

Только безопасно регистрироваться для NS/UIApplicationWillTerminateNotification в общем случае только в основном потоке - обязательно учтите это. – rpetrich

0

EDIT

Я в том числе это сверху, ниже вы можете увидеть мои исторические оригинальные вопросы и реализации.Однако я думаю, что я нашел оптимальный способ обеспечить способ sharedInstance без каких-либо стопорного над головой, я хотел бы услышать потенциальные проблемы по этому поводу:

// Volatile to make sure we are not foiled by CPU caches 
static volatile ALBackendRequestManager *sharedInstance; 

// There's no need to call this directly, as method swizzling in sharedInstance 
// means this will get called after the singleton is initialized. 
+ (MySingleton *)simpleSharedInstance 
{ 
    return (MySingleton *)sharedInstance; 
} 

+ (MySingleton*)sharedInstance 
{ 
    @synchronized(self) 
    { 
     if (sharedInstance == nil) 
     { 
      sharedInstance = [[MySingleton alloc] init]; 
      // Replace expensive thread-safe method 
        // with the simpler one that just returns the allocated instance. 
      SEL orig = @selector(sharedInstance); 
      SEL new = @selector(simpleSharedInstance); 
      Method origMethod = class_getClassMethod(self, orig); 
      Method newMethod = class_getClassMethod(self, new); 
      method_exchangeImplementations(origMethod, newMethod); 
     } 
    } 
    return (MySingleton *)sharedInstance; 
} 

И историческая дискуссия вокруг инициализации:

I теперь исходный код был скорее похож на мой (ниже), за исключением проверки экземпляра вне блокировки.

Хотя новый метод инициализации + (void) интересен, я не уверен, что мне это лучше нравится. Кажется, что теперь вы можете получить экземпляр singleton, который вы должны всегда звонить:

MySingleton instance = [[MySingleton alloc] init]; 

Неверное ли это? Это странно, и эффективнее, если вызов для инициализации уже заблокирован для вас? Метод двойной блокировки, похоже, работает нормально для этого варианта использования, а также избегает блокировки (при потенциальной стоимости двойного размещения я думаю, так как более чем один поток может провалиться через if).

Другое, что кажется странным, если, если метод initialize был действительно предпочтительным, почему мы не видим его в другом месте? Objective-C работает в течение долгого времени, и я опасаюсь основополагающих механизмов, которые отличаются от практически всех опубликованных примеров.

Мой код, который я в настоящее время использую (который отражает, что я видел в других местах, в том числе this ответ):

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

+ (id)allocWithZone:(NSZone *)zone 
{ 
    @synchronized(self) 
    { 
     if (sharedInstance == nil) 
     { 
      sharedInstance = [super allocWithZone:zone]; 
      return sharedInstance; // assignment and return on first allocation 
     } 
    } 
    return nil; // on subsequent allocation attempts return nil 
} 
+0

Интересно. Моя реализация полностью исключает блокировки, поэтому улучшается производительность и нет возможности блокировки. Кроме того, вы должны синхронизироваться в [MySingleton class], так как self еще не инициализирован. – Jacko

+0

@Jacko, это не так, 'self' будет инициализирован (то есть с' + initialize') до того, как будут отправлены какие-либо сообщения, независимо от того, вызывают ли они методы класса или экземпляра. – dreamlax

+0

В методе уровня класса «я» эквивалентно [классу MyClass] - то есть BOOL isSelf = self == [MySingleton class]; будет установлено значение YES в методе sharedInstance выше. Кроме того, поскольку код написан, нет возможности блокировки из-за последовательности вызовов ... однако я согласен, что метод «initialize», вероятно, лучше. Как вы попадаете в переменную sharedInstance извне класса? Я не видел этого метода. –

0

Ваша реализация является поточно-и, кажется, чтобы покрыть все основания (+ инициализации отправляется резьбовых безопасно по времени выполнения)

Редактировать: много кода будет небезопасным для вызова во время функции atexit. Регистрация на на основной теме безопаснее.

edit2: У меня есть дистиллированная и утонченная модель, которую я использую в макросе. -init вызывается один раз при первом вызове +sharedInstance и -dealloc будет вызываться при завершении приложения.

#define IMPLEMENT_UIAPP_SINGLETON(class_name) \ 
static class_name *shared ## class_name; \ 
+ (void)cleanupFromTerminate \ 
{ \ 
    class_name *temp = shared ## class_name; \ 
    shared ## class_name = nil; \ 
    [temp dealloc]; \ 
} \ 
+ (void)registerForCleanup \ 
{ \ 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(cleanupFromTerminate) name:UIApplicationWillTerminateNotification object:nil]; \ 
} \ 
+ (void)initialize { \ 
    if (self == [class_name class]) { \ 
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; \ 
     if ([NSThread isMainThread]) \ 
      [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(cleanupFromTerminate) name:UIApplicationWillTerminateNotification object:nil]; \ 
     else \ 
      [self performSelectorOnMainThread:@selector(registerForCleanup) withObject:nil waitUntilDone:NO]; \ 
     shared ## class_name = [[super allocWithZone:NULL] init]; \ 
     [pool drain]; \ 
    } \ 
} \ 
+ (class_name *)sharedInstance \ 
{ \ 
    return shared ## class_name; \ 
} \ 
+ (id)allocWithZone:(NSZone *)zone \ 
{ \ 
    return shared ## class_name; \ 
} \ 
- (id)copyWithZone:(NSZone *)zone \ 
{ \ 
    return self; \ 
} \ 
- (id)retain \ 
{ \ 
    return self; \ 
} \ 
- (NSUInteger)retainCount \ 
{ \ 
    return NSUIntegerMax; \ 
} \ 
- (void)release \ 
{ \ 
} \ 
- (id)autorelease \ 
{ \ 
    return self; \ 
} 
+0

Apple заявила, что не использует методы с именами, начинающимися с символов подчеркивания. Префикс подчеркивания зарезервирован и используется для частных методов Apple. – dreamlax

+0

Очень хорошая точка (согласно http://www.devworld.apple.com/iphone/library/documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocLanguageSummary.html#//apple_ref/doc/uid/TP30001163-CH3-TPXREF108); отредактировано, чтобы отразить – rpetrich

+0

, почему вам нужен пул авторесурсов в методе инициализации? – Jacko

Смежные вопросы