Вопрос по objective-c, circular-reference, iphone – У меня есть круговая ссылка. Как я могу создать слабую ссылку в Objective-C?

20

Я работаю над приложением для 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 говорится, что для того, чтобы освободить объект с циклической ссылкой, мне нужна слабая ссылка вместо сильной ссылки (свойство retain), но она не объясняет и не объясняет, как именно я это делаю. Я планирую освободить и освободить все блоки в ряду, а также сам ряд одновременно. Как настроить слабую ссылку в каждом из моих блоков на их «родительский» Строка?

Почему, спасибо! Этот сайт - именно то, что я искал во всех аспектах. Имеет смысл выделить время, чтобы задать четкий вопрос, когда так много людей готовы выскочить и ответить на него сразу. Tozar
Кстати, вас заслуживают похвалы за ваше редактирование и соответствующую пометку вашего вопроса, особенно как нового пользователя SO. Браво! Я ценю людей, которые тратят время на то, чтобы немного улучшить положение дел на благо общества. Quinn Taylor

Ваш Ответ

4   ответа
19

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

@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;

ЦитироватьКрис Хансон (где вы можете найти более подробную информацию):

"By prefixing an instance variable declaration with __weak, you tell the garbage collector that if it's the only reference to an object that the object should be considered collectable."

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

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

Error: User Rate Limit Exceeded Tozar
Error: User Rate Limit Exceeded Tozar
Error: User Rate Limit Exceeded Tozar
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded Tozar
4

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

Обычно в какао,Row сохранитBlock объекты (включая их в NSMutableArray), ноBlock не сохранил быRowкаждый из них просто сохранит его в файле ivar (со свойством «assign»).

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

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

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

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

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
3

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

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

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

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

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

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

  1. It's thread safe. There is no way you can have the weak reference contained in Subordinate become an invalid pointer. It may become nil but that is OK.
  2. Only the objects themselves need to know about the embedded weak reference. All other objects can treat Subordinate as if it has a regular reference to Superior.
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit ExceededhereError: User Rate Limit Exceeded
8

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

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

Похожие вопросы