Pergunta

Eu estou usando o padrão singleton em vários lugares em um aplicativo, e eu estou recebendo erros de vazamento de memória de clang ao analisar o código.

static MyClass *_sharedMyClass;
+ (MyClass *)sharedMyClass {
  @synchronized(self) {
    if (_sharedMyClass == nil)
      [[self alloc] init];
  }
  return _sharedMyClass;
}

// clang error: Object allocated on line 5 is no longer referenced after this point and has a retain count of +1 (object leaked)

Eu estou usando essas configurações para scan-build:

scan-build -v -v -v -V -k xcodebuild

Estou bastante certo de que o código no singleton é muito bem - afinal, é o mesmo código referenciada aqui no estouro de pilha, bem como na documentação da Apple -, mas gostaria de receber o aviso de vazamento de memória resolvido assim meu varredura-build retornos sucesso.

Foi útil?

Solução

Eu posso estar sendo excepcionalmente densa, mas certamente sua linha 5

[[self alloc] init];

aloca um objeto do tipo de classe que contém, e prontamente joga fora? Você não quer

_sharedMyClass = [[self alloc] init];

?

Outras dicas

Apple desde então atualiza o seu recomendado Singleton código para passar o analisador estático:

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

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

Agora +sharedManager e atribui o retorno de -allocWithZone: chamadas -init de super, e -allocWithZone: do singleton apenas retorna uma retidos sharedInstance.

Editar:

Por que o mantêm em + allocWithZone:?

+ allocWithZone: é substituído porque alguém usando MyGizmoClass poderia contornar o singleton chamando [[MyGizmoClass alloc] Init] em vez de [MyGizmoClass sharedManager]. É retido porque + alloc é esperado para sempre retornar um objeto com um manter a contagem de +1.

Cada chamada para + alloc deve ser equilibrada com uma -release ou -autorelease, assim, sem o reter em + allocWithZone :, a instância compartilhada pode potencialmente ser desalocada debaixo de outros usuários.

Você pode estar interessado em um simples, um método, GCD baseado em Singleton implementação (e, portanto, 10.6+ apenas) publicado em Mike Ash local :

+ (id)sharedFoo
{
    static dispatch_once_t pred;
    static Foo *foo = nil;

    dispatch_once(&pred, ^{ foo = [[self alloc] init]; });
    return foo;
}

Você está fazendo referência self em um método de classe! Big não-não! Em segundo lugar, você está chamando [[self alloc] init] e apenas jogando fora a instância. Você deve atribuir a referência singleton no método de classe, e não em init como eu estou supondo que você está fazendo. Em seguida, não há nenhuma garantia real de que _sharedMyClass será inicializado para zero. Você deve inicializar explicitamente a nil.

static MyClass *_sharedMyClass = nil;

+ (MyClass *)sharedMyClass {
  @synchronized(self) {
    if (_sharedMyClass == nil)
      _sharedMyClass = [[MyClass alloc] init];
  }
  return _sharedMyClass;
}

Você provavelmente também tinha isso em lá também ...

+ (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
}

A razão que você não estavam armazenando-o em init é porque você estava armazenando-o no método que alloc chamado. Este é o padrão Apple tem em seus exemplos. Se você salvar o valor em init bem, tudo está bem eo aviso vai embora. Eu deixaria a implementação allocWithZone sozinho.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top