当在ARC下的Objective-C上的弱引用变为nil时,如何得到通知?

时间:2021-05-18 09:43:58

Is there a mechanism which would allow an object to know that a zeroing weak reference turned nil?

是否有一种机制可以让一个对象知道零化弱引用变成了nil?

For example I have a property

例如,我有一个属性

@property (nonatomic, weak) MyClass *theObject;

when theObject deallocates and the property turns nil I want to get notified. But how? Does the zeroing weak reference system use the setter to set the property to nil when the object goes away?

当对象分配和属性变为nil时,我希望得到通知。但如何?当对象消失时,调零弱引用系统是否使用setter将属性设置为nil ?

6 个解决方案

#1


22  

The runtime just sets the weak ivar _theObect to nil, a custom setter is not called.

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

What you could do (if you really need the notification):

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

  • define a local "watcher" class and implement dealloc in that class,
  • 定义一个本地“监视者”类并在该类中实现dealloc,
  • create a watcher object and set it as "associated object" of _theObject.
  • 创建一个watcher对象,并将其设置为_theObject的“关联对象”。

When _theObject is deallocated, the associated object is released and deallocated (if there are no other strong refereces to it). Therefore its dealloc method is called. This is your "notification".

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

(I'm writing this on the phone and can fill in the details later if necessary.)

(我是在电话里写的,如果需要的话,我可以稍后填写细节。)

#2


3  

If you care when an object goes away, you shouldn't be using a weak reference. What are you trying to do?

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

#3


3  

There is no notification about object deallocation.

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

The system will not use setter method (this means no KVO notifications will be raised). The ivar is the real weak reference which gets zeroed. The weak keyword on a property is merely an instruction for synthesizing the ivar, and a public declaration that the object is not retained.

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

Though you can always invent your own notifications and send them from dealloc method of your classes, note that normally you should not ever be interested in such notifications and there is at least one good reason that they don't exist.

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

Whenever there is any kind of automatic memory management is in use, you can not (by definition) expect objects to die exactly when you need them to, that applies to Objective-C reference counting. Because any component may unexpectedly prolong lifetime of any object for unknown period of time, relying program behavior on assumption that dealloc will be called exactly when you need it to is bad design and a recipe for trouble. dealloc should be used for cleaning up only.

无论何时使用任何类型的自动内存管理,您都不能(根据定义)期望对象在您需要的时候死去,这适用于Objective-C引用计数。由于任何组件都可能意外地延长任何对象的生命周期,在未知的时间内依赖程序行为,假定dealloc将在您需要的时候被调用,这是糟糕的设计,而且会带来麻烦。dealloc应该只用于清理。

Try this rule of thumb: will the program still work correctly if dealloc does not get called at all? If not, you should rethink program's logic rather than sending out dealloc notifications.

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

#4


1  

I implemented this using a so-called weak reference registry, see the class BMWeakReferenceRegistry, part of my open source BMCommons framework for iOS.

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

This class associates context objects with the object of interest. When this object is released, so is the context object and the cleanup block is called.

这个类将上下文对象与感兴趣的对象关联起来。释放该对象时,上下文对象和清理块也会被调用。

See the API:

看到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


0  

there is no notification system for weak vars.

没有弱vars的通知系统。

#6


0  

The following is an example that I used to implement multicast of delegates. It might be useful to illustrate how to monitor the 'dealloc' of weak referenced objects (the delegates).

下面是我用来实现委托多播的一个例子。说明如何监视弱引用对象(委托)的“dealloc”可能会有用。

There will be a master DelegateRef object. Its array keeps record of all delegateRefs which wrap the real delegates. The main purpose here is to remove the strong reference to delegateRefs kept by the array when the real delegates dealloc. Therefore, a local watch object is created and associated to delegate when adding the delegate. When the local watch dealloc, the delegateRef gets removed from the master DelegateRef's array.

有一个主委托对象。它的数组记录了所有委托给真正代表的委托。这里的主要目的是删除在真正的委托dealloc时数组保存的对委托的强引用。因此,将创建一个本地监视对象,并在添加委托时关联到委托。当本地监视dealloc时,委托权函数将从主委托权函数的数组中删除。

#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

#1


22  

The runtime just sets the weak ivar _theObect to nil, a custom setter is not called.

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

What you could do (if you really need the notification):

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

  • define a local "watcher" class and implement dealloc in that class,
  • 定义一个本地“监视者”类并在该类中实现dealloc,
  • create a watcher object and set it as "associated object" of _theObject.
  • 创建一个watcher对象,并将其设置为_theObject的“关联对象”。

When _theObject is deallocated, the associated object is released and deallocated (if there are no other strong refereces to it). Therefore its dealloc method is called. This is your "notification".

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

(I'm writing this on the phone and can fill in the details later if necessary.)

(我是在电话里写的,如果需要的话,我可以稍后填写细节。)

#2


3  

If you care when an object goes away, you shouldn't be using a weak reference. What are you trying to do?

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

#3


3  

There is no notification about object deallocation.

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

The system will not use setter method (this means no KVO notifications will be raised). The ivar is the real weak reference which gets zeroed. The weak keyword on a property is merely an instruction for synthesizing the ivar, and a public declaration that the object is not retained.

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

Though you can always invent your own notifications and send them from dealloc method of your classes, note that normally you should not ever be interested in such notifications and there is at least one good reason that they don't exist.

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

Whenever there is any kind of automatic memory management is in use, you can not (by definition) expect objects to die exactly when you need them to, that applies to Objective-C reference counting. Because any component may unexpectedly prolong lifetime of any object for unknown period of time, relying program behavior on assumption that dealloc will be called exactly when you need it to is bad design and a recipe for trouble. dealloc should be used for cleaning up only.

无论何时使用任何类型的自动内存管理,您都不能(根据定义)期望对象在您需要的时候死去,这适用于Objective-C引用计数。由于任何组件都可能意外地延长任何对象的生命周期,在未知的时间内依赖程序行为,假定dealloc将在您需要的时候被调用,这是糟糕的设计,而且会带来麻烦。dealloc应该只用于清理。

Try this rule of thumb: will the program still work correctly if dealloc does not get called at all? If not, you should rethink program's logic rather than sending out dealloc notifications.

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

#4


1  

I implemented this using a so-called weak reference registry, see the class BMWeakReferenceRegistry, part of my open source BMCommons framework for iOS.

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

This class associates context objects with the object of interest. When this object is released, so is the context object and the cleanup block is called.

这个类将上下文对象与感兴趣的对象关联起来。释放该对象时,上下文对象和清理块也会被调用。

See the API:

看到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


0  

there is no notification system for weak vars.

没有弱vars的通知系统。

#6


0  

The following is an example that I used to implement multicast of delegates. It might be useful to illustrate how to monitor the 'dealloc' of weak referenced objects (the delegates).

下面是我用来实现委托多播的一个例子。说明如何监视弱引用对象(委托)的“dealloc”可能会有用。

There will be a master DelegateRef object. Its array keeps record of all delegateRefs which wrap the real delegates. The main purpose here is to remove the strong reference to delegateRefs kept by the array when the real delegates dealloc. Therefore, a local watch object is created and associated to delegate when adding the delegate. When the local watch dealloc, the delegateRef gets removed from the master DelegateRef's array.

有一个主委托对象。它的数组记录了所有委托给真正代表的委托。这里的主要目的是删除在真正的委托dealloc时数组保存的对委托的强引用。因此,将创建一个本地监视对象,并在添加委托时关联到委托。当本地监视dealloc时,委托权函数将从主委托权函数的数组中删除。

#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