I têm uma referência circular. Como posso criar uma referência fraca em Objective-C?
-
21-08-2019 - |
Pergunta
Eu estou trabalhando em um aplicativo para iPhone. Eu tenho um objeto de Row
classe que precisa para liberar numerosos objetos da Block
classe. Cada Block
atualmente tem uma propriedade que mantém uma variável de instância de Row
classe.
@interface Block : UIImageView {
Row *yCoord;
}
@property (nonatomic,retain) Row *yCoord;
@end
Cada Row
contém um NSMutableArray
desses blocos.
@interface Row : NSObject {
NSMutableArray *blocks;
}
-(void)addBlock:(Block*)aBlock;
@end
@implementation Row
-(void)addBlock:(Block*)aBlock {
[blocks addObject:aBlock];
aBlock.yCoord = self;
}
@end
Eu entendo que esta é uma referência circular. estados de documentação da Apple que, a fim de retirar atribuição de um objeto com uma referência circular Eu preciso de uma referência fraca, em vez de uma referência forte (a reter propriedade), mas não acompanhar, através e explicar exatamente como eu vou fazer isso. Eu pretendo liberar e Dealloc todos os blocos dentro de uma linha, bem como o próprio Row simultaneamente. Como faço para configurar uma referência fraca dentro de cada um de meus blocos ao seu "pai" Row?
Solução
Editar: Desde o consulente esclareceu que ele não está usando a coleta de lixo (iPhone atualmente não suporta-lo), o meu conselho é para evitar ciclos por ter apenas um dos objetos reter o outro, assim como você faria com um delegado. Ao usar propriedades, use "Assign" em vez de "manter" para conseguir isso. Por exemplo:
@property (nonatomic,assign) Row *yCoord;
O resto da resposta resposta refere-se a "referências fracas" em termos de objectivo C-2.0 e GC.
Quando você está trabalhando com coleta de lixo (10.5+), uma referência fraca é criado por prefixar uma declaração de variável com __weak
. Quando você atribui a essa variável, o GC (se habilitado) mantém o controle da referência e será zero-lo para você automaticamente se todas as fortes referências ao objeto referenciado desaparecer. (Se GC não está habilitado, o atributo __weak
é ignorado.)
Assim, você pode modificar com segurança a resposta acima para jogar mais agradável com a coleta de lixo (atualmente em 10.5+, e talvez algum dia no iPhone) como segue: (Veja o docs da Apple relacionados .)
@property (nonatomic,assign) __weak Row *yCoord;
Para citar Chris Hanson (onde você pode encontrar informações mais detalhadas):
"Ao prefixar uma declaração instância variável com
__weak
, você diz o coletor de lixo que se é a única referência a um objeto que o objeto deve ser considerado colecionáveis."
Eu esclarecer que ao dizer "se não há referências não-fracas para um objeto". Assim que a última referência forte é removido, o objeto pode ser coletado, e todas as referências fracas serão zerados automaticamente.
Nota: Isto não é diretamente relacionadas com a criação de referências fracas, mas há também um __strong
atributo, mas desde que Objective-C variáveis ??objetos são fortes referências por padrão, é geralmente usado apenas para ponteiros C brutos para coisas como estruturas ou primitivos que o coletor de lixo não vai tratar como raízes, e será recolhido debaixo de você se você não declará-los como forte. (Considerando que a falta de __weak
pode causar manter ciclos e vazamentos de memória, a falta de __strong
pode resultar em stomping memória e erros realmente estranhos e insidiosas que ocorrem não-determinística e pode ser muito difícil de rastrear.)
Outras dicas
Apenas mudá-lo para atribuir em vez de reter, referências não mais circular.
@interface Block : UIImageView {
Row *yCoord;
}
@property (nonatomic,assign) Row *yCoord;
@end
Uma referência fraca é simplesmente uma atribuição (a menos que você está falando de lixo coleção que é uma lata inteira separada de vermes, mas não sofre de manter ciclos).
Normalmente, em Cocoa, Row
reteria os objetos Block
(incluindo-os no NSMutableArray), mas Block
não reteria Row
, cada um seria simplesmente armazená-lo em um ivar (com uma propriedade "Atribuir").
Enquanto Row
tem o cuidado de soltar cada Block
antes de ser desalocada (isto é, sua dealloc
deve liberar o NSMutableArray que vai liberar os blocos, enquanto ninguém mais tem qualquer ponteiros para eles) então tudo vai ser desalocados conforme apropriado .
Você também pode tomar a precaução de zerar a referência de linha de blocos antes de retirar os entiries a partir da matriz, algo como:
- (void) dealloc {
for (Block* b in _blocks) {
b.row = nil;
}
[_blocks release];
[super dealloc];
}
onde _blocks é o ivar referenciado pela propriedade blocos.
Usando assign para criar referências fracas pode não ser seguro num sistema de vários segmentos, em particular quando um ou outro objecto pode ser retido por um terceiro objecto, e, em seguida, utilizada para cancelar o outro objeto.
Felizmente, esta é muitas vezes um problema de hierarquia, e o objecto de referência que contém a fraco só se preocupa com o objecto que se refere a pelo referido a-vida de objecto. Esta é a situação habitual com uma Superior <->. Relação de subordinação
Eu acho que o caso em comentário do OP mapeia para isso, com Row = Superior, Bloco = subordinado.
Neste caso, gostaria de usar um identificador para se referir ao Superior do subordinado:
// Superior.h
@class Superior;
@interface SuperiorHandle : NSObject {
@private
Superior* superior_;
}
// note the deliberate avoidance of "nonatomic"
@property (readonly) Superior *superior;
@end
@interface Superior : NSObject {
@private
SuperiorHandle *handle_;
// add one or more references to Subordinate instances
}
// note the deliberate avoidance of "nonatomic"
@property (readonly) SuperiorHandle *handle;
@end
// Superior.m
#import "Superior.h"
@implementation SuperiorHandle
@synthesize
superior = superior_;
- (id)initWithSuperior:(Superior *)superior {
if ((self = [super init])) {
superior_ = superior; // weak reference
}
}
- (void)invalidate {
@synchronized (self) {
superior_ = nil;
}
}
- (Superior *)superior {
@synchronized (self) {
// retain and autorelease is required to prevent dealloc before we're ready, thanks to AndroidDev for pointing out this mistake
return [[superior_ retain] autorelease];
}
}
@end
@implementation Superior
@synthesize
handle = handle_;
- (id)init {
if ((self = [super init])) {
handle_ = [[SuperiorHandle alloc] initWithSuperior:self];
}
return self;
}
- (void)dealloc {
[handle_ invalidate];
[handle_ release];
[super dealloc];
}
@end
// Subordinate.h
@class Superior;
@class SuperiorHandle;
@interface Subordinate : NSObject {
@private
SuperiorHandle *superior_handle_;
}
@property (readonly) Superior *superior;
@end
// Subordinate.m
#import "Subordinate.h"
#import "Superior.h"
@implementation Subordinate
// no synthesize this time, superior's implementation is special
- (id)initWithSuperior:(Superior *)superior {
if ((self = [super init])) {
superior_handle_ = [superior.handle retain];
}
return self;
}
- (void)dealloc {
[superior_handle_ release];
[super dealloc];
}
- (Superior *)superior {
@synchronized (superior_handle_) {
return superior_handle_.superior;
}
}
@end
Algumas vantagens:
- É de thread-safe. Não há nenhuma maneira você pode ter a referência fraca contida no Subordinate tornar-se um ponteiro inválido. Pode tornar-se nula, mas que é OK.
- Apenas os próprios objetos precisa saber sobre a referência fraca incorporado. Todos os outros objetos pode tratar subordinado como se tem uma referência regular para Superior.