Objectivo-C Singleton e LLVM / avisos de vazamento clang
-
06-07-2019 - |
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.
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.