23

是否有一种机制可以让对象知道归零弱引用变为 nil?

例如我有一个财产

@property (nonatomic, weak) MyClass *theObject;

当 theObject 解除分配并且属性变为 nil 时,我想得到通知。但是怎么做?当对象消失时,归零弱参考系统是否使用 setter 将属性设置为 nil?

4

8 回答 8

25

运行时只是将弱 ivar _theObect 设置为 nil,不会调用自定义设置器。

你可以做什么(如果你真的需要通知):

  • 定义一个本地“观察者”类并在该类中实现 dealloc,
  • 创建一个观察者对象并将其设置为 _theObject 的“关联对象”。

当 _theObject 被释放时,关联的对象被释放和释放(如果没有其他强引用它)。因此它的 dealloc 方法被调用。这是您的“通知”。

(我在手机上写这个,如果有需要可以稍后填写。)

于 2013-10-13T11:06:41.400 回答
3

没有关于对象释放的通知。

系统不会使用 setter 方法(这意味着不会引发 KVO 通知)。ivar 是真正的弱引用,它被归零。属性上的weak关键字只是合成 ivar 的指令,以及不保留对象的公开声明。

尽管您总是可以发明自己的通知并从dealloc类的方法发送它们,但请注意,通常您不应该对此类通知感兴趣,并且至少有一个很好的理由表明它们不存在。

无论何时使用任何类型的自动内存管理,您都不能(根据定义)期望对象在您需要它们时准确地死亡,这适用于 Objective-C 引用计数。因为任何组件都可能意外地将任何对象的生命周期延长到未知的时间段,所以将程序行为依赖于dealloc将在您需要时准确调用它的假设是糟糕的设计和麻烦的秘诀。dealloc只能用于清理。

试试这个经验法则:如果dealloc根本没有被调用,程序还能正常工作吗?如果没有,您应该重新考虑程序的逻辑,而不是发送 dealloc 通知。

于 2013-10-13T11:59:30.583 回答
3

如果您关心对象何时消失,则不应使用弱引用。你想做什么?

于 2013-10-13T11:28:05.820 回答
2

我使用所谓的弱引用注册表实现了这一点,请参阅BMWeakReferenceRegistry类,它是我的 iOS 开源 BMCommons 框架的一部分。

此类将上下文对象与感兴趣的对象相关联。当这个对象被释放时,上下文对象和清理块被调用。

请参阅 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
于 2014-04-07T14:53:28.757 回答
2

根据Martin R回答,我想出了以下代码段。只要确保你的 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()
    }
}

初始化弱变量时这样调用它:

self.weakVar = obj
WeakWatcher.watch(obj, onDeinit: { /* do something */ })
于 2019-06-25T18:23:26.047 回答
0

弱变量没有通知系统。

于 2013-10-13T10:47:04.177 回答
0

以下是我用来实现委托多播的示例。说明如何监控弱引用对象(委托)的“dealloc”可能很有用。

会有一个主 DelegateRef 对象。它的数组记录了包装真实代表的所有 delegateRefs。这里的主要目的是在真正的委托dealloc时删除数组保存的对delegateRefs的强引用。因此,在添加委托时会创建一个本地监视对象并将其关联到委托。当本地监视解除分配时,delegateRef 会从主 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
于 2016-08-23T07:18:58.010 回答
0

Apple通过使用他们的私有类在UIPageViewController' 的弱属性上实现了这一点,但您可以轻松地实现它。在setter 中,他们创建了一个实例,并且在弱/强舞蹈之后调用块以避免保留循环。然后他们调用设置弱辅助对象的 dataSource 对象。最后在他们调用释放块。它之所以有效,是因为当被释放时,关联也会被释放。dataSource_UIWeakHelpersetDataSource[_UIWeakHelper.alloc initWithDeallocationBlock:block]self.dataSource = nilobjc_setAssociatedObject_UIWeakHelper deallocdataSource_UIWeakHelper

如果你想知道他们为什么需要这个,那是因为当dataSource他们想要禁用页面滚动时,因为没有页面可以滚动到。

只是不要犯 Apple 犯过的同样错误(从 iOS 13.4.1 Xcode 11.4.1 开始),他们将帮助对象关联到相同的键,dataSource因此delegate只有一个释放块被触发,呵呵!

于 2020-05-01T13:14:36.863 回答