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

Использование объекта objc_setAssociatedObject со слабыми ссылками

Я знаю, что OBJC_ASSOCIATION_ASSIGN существует, но нулевая ссылка, если целевой объект освобожден? Или это похоже на старые времена, когда эта ссылка должна быть обновлена ​​или мы рискуем плохой доступ позже?

4b9b3361

Ответ 1

После проверки, ответ НЕТ.

Я запускал следующий код под симулятором iOS 6, но он, вероятно, имел бы такое же поведение с предыдущими итерациями среды выполнения:

NSObject *test1 = [NSObject new];

NSObject __weak *test2 = test1;

objc_setAssociatedObject(self, "test", test1, OBJC_ASSOCIATION_ASSIGN);

test1 = nil;

id test3 = objc_getAssociatedObject(self, "test");

В конце концов, test1 и test2 равны нулю, а test3 - указатель, ранее сохраненный в test1. Использование test3 приведет к попытке получить доступ к объекту, который уже был освобожден.

Ответ 2

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

@interface WeakObjectContainer : NSObject
@property (nonatomic, readonly, weak) id object;
@end

@implementation WeakObjectContainer
- (instancetype) initWithObject:(id)object
{
    if (!(self = [super init]))
        return nil;

    _object = object;

    return self;
}
@end

Затем вы должны связать WeakObjectContainer как OBJC_ASSOCIATION_RETAIN (_NONATOMIC):

objc_setAssociatedObject(self, &MyKey, [[WeakObjectContainer alloc] initWithObject:object], OBJC_ASSOCIATION_RETAIN_NONATOMIC);

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

id object = [objc_getAssociatedObject(self, &MyKey) object];

Ответ 3

Еще один вариант, похожий на WeakObjectContainer:

- (id)weakObject {
    id (^block)() = objc_getAssociatedObject(self, @selector(weakObject));
    return (block ? block() : nil);
}

- (void)setWeakObject:(id)object {
    id __weak weakObject = object;
    id (^block)() = ^{ return weakObject; };
    objc_setAssociatedObject(self, @selector(weakObject),
                             block, OBJC_ASSOCIATION_COPY);
}

Ответ 4

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

В общем случае нет необходимости nil выводить ссылки в iVars во время -dealloc. Если объект отменен, не имеет значения, были ли его iVars обнулены, поскольку любой последующий доступ к освобожденному объекту или его iVars сам по себе является ошибкой программирования. На самом деле, я слышал, что некоторые утверждают, что лучше не удалять ссылки во время -dealloc, потому что это сделает ошибки более очевидными/покажут ошибки раньше.

EDIT: О, я думаю, я неправильно понял ваш вопрос. Вы хотите "обнулить слабые ссылки". Соответствующее хранилище, похоже, не поддерживает их. Вы можете сделать тривиальный проходной класс с одним ivar/свойством, обозначенным как __weak, и добиться такого же эффекта. Немного клодэй, но это сработает.

Ответ 5

Быстрая версия ответа 0xced (с поддержкой типов):

public class WeakObjectContainer<T: AnyObject>: NSObject {

    private weak var _object: T?

    public var object: T? {
        return _object
    }

    public init(with object: T?) {
        _object = object
    }

}