这是一个最小的工作想法(我也对 KVO 风格感到厌烦)。想法是在 KVO 上下文中携带块,然后在触发观察时调用它。
// NSObject+KVOBlock.h
#import <Foundation/Foundation.h>
@interface NSObject (KVOBlock)
// invoke the block when the receiver's value at keyPath changes
// block params are the receiver, the keyPath and the old value
- (void)observeKeyPath:(NSString *)keyPath withBlock:(void (^)(id, NSString *, id))block;
- (void)unobserveKeyPath:(NSString *)keyPath;
@end
// NSObject+KVOBlock.m
#import "NSObject+KVOBlock.h"
@implementation NSObject (KVOBlock)
- (void)observeKeyPath:(NSString *)keyPath withBlock:(void (^)(id, NSString *, id))block {
[self addObserver:self forKeyPath:keyPath
options:NSKeyValueObservingOptionOld
context:(__bridge void *)(block)];
}
- (void)unobserveKeyPath:(NSString *)keyPath {
[self removeObserver:self forKeyPath:keyPath];
}
- (void) observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context {
void (^block)(id, NSString *, id) = (__bridge void (^)(id, NSString *, id))context;
block(self, keyPath, [change objectForKey:NSKeyValueChangeOldKey]);
}
@end
这样称呼...
// assume a class called SomeObject with an instance called someObject
someObject.someProperty = @"Bar";
[someObject observeKeyPath:@"someProperty" withBlock:^(SomeObject *object, NSString *keyPath, NSString *oldValue) {
// avoid referring directly to 'someObject' in this block, since it retains
// the block via the kvo context, thereby causing a retain cycle. The first
// param ('object') is exactly equal to someObject. So use that instead.
NSLog(@"object=%@, keyPath=%@, oldValue=%@, newValue=%@",
object, keyPath, oldValue, object.someProperty);
}];
// at any point after this, when you change someProperty, the block will be invoked
self.object.someProperty = @"Foo";
我用上面的代码做了一个小测试,并确认它至少在此处显示的情况下有效。控制台输出看起来像这样...
<SomeObject :0xblahblah>, keyPath=someProperty, oldValue=Bar, newValue=Foo