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?

Foi útil?

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:

  1. É 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.
  2. 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.
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top