Подтвердить что ты не робот

У меня циркулярная ссылка. Как создать слабую ссылку в Objective-C?

Я работаю над iPhone-приложением. У меня есть объект класса Row, который должен выпустить многочисленные объекты класса Block. Каждый Block имеет свойство, которое сохраняет переменную экземпляра класса Row.

@interface Block : UIImageView {
  Row *yCoord;
}
@property (nonatomic,retain) Row *yCoord;
@end

Каждый Row содержит NSMutableArray этих блоков.

@interface Row : NSObject {
    NSMutableArray *blocks;
}
-(void)addBlock:(Block*)aBlock;
@end

@implementation Row
-(void)addBlock:(Block*)aBlock {
    [blocks addObject:aBlock];
    aBlock.yCoord = self;
}
@end

Я понимаю, что это круговая ссылка. Документация Apple гласит, что для того, чтобы освободить объект с помощью циклической ссылки, мне нужна слабая ссылка вместо сильной ссылки (свойство сохранения), но она не проходит и объясняет, как именно я это делаю. Я планирую выпустить и исключить все блоки внутри строки, а также сам ряд одновременно. Как настроить слабую ссылку в каждом из моих блоков на их родительскую строку?

4b9b3361

Ответ 1

Изменить:. Поскольку искатель уточнил, что он не использует сбор мусора (iPhone в настоящее время его не поддерживает), мой совет - избегать циклов, если только один из объектов сохраняет другой, так же, как вы будет делать с делегатом. При использовании свойств используйте "присваивать" вместо "сохранить" для достижения этого. Например:

@property (nonatomic,assign) Row *yCoord;

Остальная часть ответа на ответ относится к "слабым ссылкам" в терминах Objective-C 2.0 и GC.


Когда вы работаете с сборкой мусора (10.5+), слабая ссылка создается путем префикса объявления переменной с помощью __weak. Когда вы назначаете эту переменную, GC (если включен) отслеживает ссылку и автоматически отключит ее для вас, если все сильные ссылки на указанный объект исчезнут. (Если GC не включен, атрибут __weak игнорируется.)

Таким образом, вы можете безопасно изменить приведенный выше ответ, чтобы лучше играть с сборкой мусора (в настоящее время на 10.5+ и, возможно, когда-нибудь на iPhone) следующим образом: (См. связанные документы Apple.)

@property (nonatomic,assign) __weak Row *yCoord;

Чтобы процитировать Chris Hanson (где вы можете найти более подробную информацию):

"Префикс объявления переменной экземпляра с помощью __weak, вы сообщаете сборщику мусора, что если это единственная ссылка на объект, объект должен считаться собираемым."

Я бы уточнил это, сказав "если нет никаких слабых ссылок на объект". Как только последняя сильная ссылка удаляется, объект может быть собран, и все слабые ссылки будут автоматически обнулены.

Примечание.. Это не связано напрямую с созданием слабых ссылок, но также существует атрибут __strong, но поскольку переменные объекта Objective-C являются сильными ссылками по умолчанию, он обычно используется только для сырых указателей C к таким вещам, как структуры или примитивы, которые сборщик мусора не будет рассматривать как корни, и будут собираться из-под вас, если вы не объявите их сильными. (В то время как отсутствие __weak может привести к задержкам циклов и утечкам памяти, отсутствие __strong может привести к тому, что память начнет топать и действительно странные и коварные ошибки, которые происходят недетерминистически и могут быть довольно трудно отследить.)

Ответ 2

Просто измените его, чтобы назначить вместо сохранения, не более круговых ссылок.

@interface Block : UIImageView {
  Row *yCoord;
}
@property (nonatomic,assign) Row *yCoord;
@end

Ответ 3

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

Обычно в Cocoa Row будут сохраняться объекты Block (включая их в NSMutableArray), но Block не сохранит Row, каждый будет просто хранить его в ivar (с свойство "присваивать" ).

Пока Row осторожно освобождает каждый Block до его освобождения (т.е. его dealloc должен освободить NSMutableArray, который освободит блоки, пока никто другой не имеет указателей на них), тогда все будет освобождено по мере необходимости.

Вы также можете принять меры предосторожности при обнулении ссылки на строку из блоков перед удалением полномочий из массива, например:

- (void) dealloc {
    for (Block* b in _blocks) {
        b.row = nil;
    }
    [_blocks release];
    [super dealloc];
}

где _blocks - это ivar, на который ссылается свойство блоков.

Ответ 4

Использование назначения для создания слабых ссылок может быть небезопасным в многопоточной системе, особенно когда любой объект может быть сохранен третьим объектом, а затем используется для разыменования другого объекта.

К счастью, это часто является проблемой иерархии, а объект, содержащий слабую ссылку, заботится только об объекте, к которому он относится, для времени жизни упомянутого объекта. Это обычная ситуация с отношением Superior ↔ Subordinate.

Я думаю, что случай в комментарии OP соответствует этому, с Row = Superior, Block = Subordinate.

В этом случае я бы использовал дескриптор для ссылки на Superior из подчиненного:

// 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 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

Некоторые преимущества:

  • Он потокобезопасен. Невозможно, чтобы слабая ссылка, содержащаяся в Subordinate, стала недопустимым указателем. Это может стать нулевым, но это нормально.
  • Только сами объекты должны знать о встроенной слабой ссылке. Все остальные объекты могут обрабатывать Subordinate, как если бы он имел регулярную ссылку на Superior.