我想它触发一次后添加志愿观察发现删除自身。 我见过很多在计算器上做的乡亲这样的东西的:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:@"myKeyPath"])
{
NSLog(@"Do stuff...");
[object removeObserver:self forKeyPath:@"isFinished"];
}
else
{
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
这似乎是合理的,但我知道,调用-removeObserver:forKeyPath:
从内部-observeValueForKeyPath:...
可导致不确定性的崩溃是难以调试 。 我也希望确保这种观察只被调用一次(或者根本没有,如果永远不会发送通知)。 什么是做到这一点的好办法?
我在这里回答我的问题,因为我已经看到了所有的地方问题的模式,但还没有到一个更好的方式一个很好的例子的参考。 我已经失去了天,如果不是几个星期,我的生活的调试,最终发现通过添加和国际志愿者组织的通知的传递过程中去除观察员引起的问题。 如果没有保修,我提出以下实施一次性的志愿通知应避免来自调用问题-addObserver:...
和-removeObserver:...
从内-observeValueForKeyPath:...
。 编码:
NSObject的+ KVOOneShot.h:
typedef void (^KVOOneShotObserverBlock)(NSString* keyPath, id object, NSDictionary* change, void* context);
@interface NSObject (KVOOneShot)
- (void)addKVOOneShotObserverForKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context block: (KVOOneShotObserverBlock)block;
@end
NSObject的+ KVOOneShot.m:(与-fno-objc弧编译,所以我们可以更明确一些保留/释放)
#import "NSObject+KVOOneShot.h"
#import <libkern/OSAtomic.h>
#import <objc/runtime.h>
@interface KVOOneShotObserver : NSObject
- (instancetype)initWithBlock: (KVOOneShotObserverBlock)block;
@end
@implementation NSObject (KVOOneShot)
- (void)addKVOOneShotObserverForKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context block: (KVOOneShotObserverBlock)block
{
if (!block || !keyPath)
return;
KVOOneShotObserver* observer = nil;
@try
{
observer = [[KVOOneShotObserver alloc] initWithBlock: block];
// Tie the observer's lifetime to the object it's observing...
objc_setAssociatedObject(self, observer, observer, OBJC_ASSOCIATION_RETAIN);
// Add the observation...
[self addObserver: observer forKeyPath: keyPath options: options context: context];
}
@finally
{
// Make sure we release our hold on the observer, even if something goes wrong above. Probably paranoid of me.
[observer release];
}
}
@end
@implementation KVOOneShotObserver
{
void * volatile _block;
}
- (instancetype)initWithBlock: (KVOOneShotObserverBlock)block
{
if (self = [super init])
{
_block = [block copy];
}
return self;
}
- (void)dealloc
{
[(id)_block release];
[super dealloc];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
KVOOneShotObserverBlock block = (KVOOneShotObserverBlock)_block;
// Get the block atomically, so it can only ever be executed once.
if (block && OSAtomicCompareAndSwapPtrBarrier(block, NULL, &self->_block))
{
// Do it.
@try
{
block(keyPath, object, change, context);
}
@finally
{
// Release it.
[block release];
// Remove the observation whenever...
// Note: This can potentially extend the lifetime of the observer until the observation is removed.
dispatch_async(dispatch_get_main_queue(), ^{
[object removeObserver: self forKeyPath: keyPath context: context];
});
// Don't keep us alive any longer than necessary...
objc_setAssociatedObject(object, self, nil, OBJC_ASSOCIATION_RETAIN);
}
}
}
@end
这里唯一的潜在结是该dispatch_async
延迟去除可以略微由主运行循环中的一个通延伸的观察对象的寿命。 这不应该是在通常情况下一个大问题,但它是值得一提。 我最初的想法是,除去在观察dealloc
,但我的理解是,我们没有一个强有力的保证,被观察对象仍然活着,当-dealloc
的KVOOneShotObserver
被调用。 从逻辑上讲,应该是这样的情况下,由于被观察的对象将有只“看到”保留,但由于我们把这个对象传递到API,它的实现,我们看不到的,我们不能完全肯定。 鉴于此,这种感觉就像是最安全的方式。