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

Как получить уведомление, когда слабая ссылка обнуления становится нулевой на Objective-C в ARC?

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

Например, у меня есть свойство

@property (nonatomic, weak) MyClass *theObject;

когда объект Odeject освобождается и свойство меняет нуль, я хочу получить уведомление. Но как? Использует ли эталонная система обнуления с помощью установщика установить свойство в nil, когда объект уходит?

4b9b3361

Ответ 1

Время выполнения просто устанавливает слабый ivar _theObect равным нулю, пользовательский сеттер не вызывается.

Что вы могли бы сделать (если вам действительно нужно уведомление):

  • определить локальный класс "наблюдателя" и реализовать dealloc в этом классе,
  • создать объект наблюдателя и установить его как "связанный объект" объекта _theObject.

Когда _theObject освобождается, связанный объект освобождается и освобождается (если для него нет других сильных рефезеров). Поэтому его метод dealloc вызывается. Это ваше "уведомление".

(Я пишу это по телефону и при необходимости могу заполнить детали позже.)

Ответ 2

Если вам интересно, когда объект уходит, вы не должны использовать слабую ссылку. Что вы пытаетесь сделать?

Ответ 3

Нет уведомления об освобождении объекта.

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

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

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

Попробуйте это эмпирическое правило: будет ли программа работать корректно, если dealloc вообще не вызвана? Если нет, вы должны пересмотреть логику программы, а не отправлять уведомления dealloc.

Ответ 4

Я реализовал это, используя так называемый слабый реестр ссылок, см. класс BMWeakReferenceRegistry, часть моей открытой базы BMCommons для iOS.

Этот класс связывает объекты контекста с объектом интереса. Когда этот объект освобождается, также вызывается объект контекста и блок очистки.

См. API:

/**
 * Registry for monitoring the deallocation of objects of interest to perform cleanup logic once they are released.
 */
@interface BMWeakReferenceRegistry : BMCoreObject

BM_DECLARE_DEFAULT_SINGLETON

/**
 * Cleanup block definition
 */
typedef void(^BMWeakReferenceCleanupBlock)(void);

/**
 * Registers a reference for monitoring with the supplied cleanup block.
 * The cleanup block gets called once the reference object gets deallocated.
 *
 * It is possible to register the same reference multiple times with different cleanup blocks (even if owner is the same).
 * If this is not intended behavior, check hasRegisteredReference:forOwner: before calling this method.
 *
 * @param reference The object to monitor
 * @param owner An optional owner (may be specified to selectively deregister references)
 * @param cleanup The cleanup block
 */
- (void)registerReference:(id)reference forOwner:(id)owner withCleanupBlock:(BMWeakReferenceCleanupBlock)cleanup;

/**
 * Deregisters the specified reference for monitoring. If owner is not nil, only the monitor(s) for the specified owner is/are removed.
 *
 * @param reference The monitored reference
 * @param owner The optional owner of the reference
 */
- (void)deregisterReference:(id)reference forOwner:(id)owner;

/**
 * Checks whether a monitor already exists for the specified reference/owner. If the owner parameter is nil all owners are checked.
 *
 * @param reference The monitored reference
 * @param owner The optional owner
 * @return True if registered, false otherwise.
 */
- (BOOL)hasRegisteredReference:(id)reference forOwner:(id)owner;

@end

Ответ 5

для слабых варов нет системы уведомлений.

Ответ 6

Ниже приведен пример, который я использовал для реализации многоадресной передачи делегатов. Возможно, было бы полезно проиллюстрировать, как контролировать "dealloc" слабых ссылочных объектов (делегатов).

Будет создан главный объект DelegateRef. Его массив сохраняет записи всех делегатов, которые обертывают реальных делегатов. Основная цель здесь - удалить сильную ссылку на делегирование, сохраняемое массивом, когда действительные делегаты dealloc. Таким образом, создается локальный объект наблюдения и ассоциируется с делегатом при добавлении делегата. Когда локальные часы dealloc, делегатRef удаляется из основного массива DelegateRef.

#import <objc/runtime.h>
@interface WeakWatcher : NSObject
@property (nonatomic, weak) NSMutableArray *masterarray;
@property (nonatomic, weak) DelegateRef *delegateRef;
@end
@implementation WeakWatcher
-(void)dealloc
{ // when the object dealloc, this will be called
    if(_delegateRef != nil)
    {
        if([self.masterarray containsObject:_delegateRef])
        {
            [_masterarray removeObject:_delegateRef];
        }
    }
}
@end

@interface DelegateRef()
@end

@implementation DelegateRef
static char assoKey[] = "assoKey";

- (NSMutableArray *)array {
    if (_array == nil) {
        _array = [NSMutableArray array];
    }
    return _array;
}

-(void)addWeakRef:(id)ref
{
    if (ref == nil) {
        return;
    }
    DelegateRef *delRef = [DelegateRef new];
    WeakWatcher* watcher = [WeakWatcher new];  // create local variable
    watcher.delegateRef = delRef;
    watcher.masterarray = self.array;

    [delRef setDelegateWeakReference:ref];
    objc_setAssociatedObject(ref, assoKey, watcher, OBJC_ASSOCIATION_RETAIN);

    [self.array addObject:delRef];
}
@end

Ответ 7

Основываясь на ответе Мартина Р., я придумал следующий фрагмент. Просто убедитесь, что вы не создаете никаких циклов сохранения с помощью своего закрытия onDeinit!

private var key: UInt8 = 0

class WeakWatcher {
    private var onDeinit: () -> ()

    init(onDeinit: @escaping () -> ()) {
        self.onDeinit = onDeinit
    }

    static func watch(_ obj: Any, onDeinit: @escaping () -> ()) {
        watch(obj, key: &key, onDeinit: onDeinit)
    }

    static func watch(_ obj: Any, key: UnsafeRawPointer, onDeinit: @escaping () -> ()) {
        objc_setAssociatedObject(obj, key, WeakWatcher(onDeinit: onDeinit), objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
    }

    deinit {
        self.onDeinit()
    }
}

Назовите это так при инициализации вашего слабого VAR:

self.weakVar = obj
WeakWatcher.watch(obj, onDeinit: { /* do something */ })