Я знаю, что OBJC_ASSOCIATION_ASSIGN существует, но нулевая ссылка, если целевой объект освобожден? Или это похоже на старые времена, когда эта ссылка должна быть обновлена или мы рискуем плохой доступ позже?
Использование объекта objc_setAssociatedObject со слабыми ссылками
Ответ 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
}
}