kvo机制可以设置被监听者和监听者,当被监听者的某个属性
发生变化时,调用监听者的相关方法。
1 2 3 4 5 6 7 8 9 10 11
| [self.radioRecordArray addObserver:self forKeyPath:@"downloadDidFinished" options:NSKeyValueObservingOptionNew context:Nil];
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ if([keyPath isEqualToString:@"downloadDidFinished"]){ dispatch_async(dispatch_get_main_queue(), ^{ [self.tableView reloadData]; }); } }
|
以上实现的是异步下载,下载完毕在主线程更新view。
注意NSArray NSMutableArray均不支持kvo,可以创建一个NSArrayController来实现。
Swift中的kvo
要在被观察的变量前加 dynamic
KVO的实现
https://mikeash.com/pyblog/friday-qa-2009-01-23.html
KVO
是通过Objective-C的运行时实现的.当你观察某个类的实例化对象,KVO会在运行时创建一个被观察对象的类的子类.在这个新的类中,被观察属性的set方法将被重载.之后,被观察对象的isa指针将被替换,所以被观察对象就变成了该新的类的对象.被重载的方法负责通知观察者.
苹果并不希望这个机制被暴露,所以子类还重载了 -class
方法,当你调用这个方法时,返回的将是原本的类(本应该返回子类 o( ̄ヘ ̄*o) 机制的苹果).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
|
#import <Foundation/Foundation.h> #import <objc/runtime.h>
@interface TestClass : NSObject { int x; int y; int z; } @property int x; @property int y; @property int z; @end @implementation TestClass @synthesize x, y, z; @end
static NSArray *ClassMethodNames(Class c) { NSMutableArray *array = [NSMutableArray array]; unsigned int methodCount = 0; Method *methodList = class_copyMethodList(c, &methodCount); unsigned int i; for(i = 0; i < methodCount; i++) [array addObject: NSStringFromSelector(method_getName(methodList[i]))]; free(methodList); return array; }
static void PrintDescription(NSString *name, id obj) { NSString *str = [NSString stringWithFormat: @"%@: %@\n\tNSObject class %s\n\tlibobjc class %s\n\timplements methods <%@>", name, obj, class_getName([obj class]), class_getName(obj->isa), [ClassMethodNames(obj->isa) componentsJoinedByString:@", "]]; printf("%s\n", [str UTF8String]); }
int main(int argc, char **argv) { [NSAutoreleasePool new]; TestClass *x = [[TestClass alloc] init]; TestClass *y = [[TestClass alloc] init]; TestClass *xy = [[TestClass alloc] init]; TestClass *control = [[TestClass alloc] init]; [x addObserver:x forKeyPath:@"x" options:0 context:NULL]; [xy addObserver:xy forKeyPath:@"x" options:0 context:NULL]; [y addObserver:y forKeyPath:@"y" options:0 context:NULL]; [xy addObserver:xy forKeyPath:@"y" options:0 context:NULL]; PrintDescription(@"control", control); PrintDescription(@"x", x); PrintDescription(@"y", y); PrintDescription(@"xy", xy); printf("Using NSObject methods, normal setX: is %p, overridden setX: is %p\n", [control methodForSelector:@selector(setX:)], [x methodForSelector:@selector(setX:)]); printf("Using libobjc functions, normal setX: is %p, overridden setX: is %p\n", method_getImplementation(class_getInstanceMethod(object_getClass(control), @selector(setX:))), method_getImplementation(class_getInstanceMethod(object_getClass(x), @selector(setX:)))); return 0; }
|
ClassMethodNames
使用了Objective-C运行时方法来取得一个类所实现了的方法表.它只会取得直接在类中实现的方法,而不会取得在父类中实现的方法.PrintDescription
将对象的信息打印出来.它通过两种方法,-class
方法和一个Objective-C运行时方法,告诉我们该对象的类是什么,以及告诉我们该类中的方法的信息.
结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| control: <TestClass: 0x104b20> NSObject class TestClass libobjc class TestClass implements methods <setX:, x, setY:, y, setZ:, z> x: <TestClass: 0x103280> NSObject class TestClass libobjc class NSKVONotifying_TestClass implements methods <setY:, setX:, class, dealloc, _isKVOA> y: <TestClass: 0x104b00> NSObject class TestClass libobjc class NSKVONotifying_TestClass implements methods <setY:, setX:, class, dealloc, _isKVOA> xy: <TestClass: 0x104b10> NSObject class TestClass libobjc class NSKVONotifying_TestClass implements methods <setY:, setX:, class, dealloc, _isKVOA> Using NSObject methods, normal setX: is 0x195e, overridden setX: is 0x195e Using libobjc functions, normal setX: is 0x195e, overridden setX: is 0x96a1a550
|
通过-class
方法取得的类是TestClass
,通过object_getClass
取得是是该对象的真面目:一个NSKVONotifying_TestClass
的对象.这就是那个动态创建的类.
在NSKVONotifying_TestClass
中重载了-setX
,-setY
方法,而并没有-setZ
方法,因为没对象观察Z这个属性.同时,要留意所有被观察的TestClass
类的对象都是被替换为同一个新的子类,意味着它们都拥有这些重载方法.例如,即使y对象只是其Y属性被观察,可是其-setX
方法也被重载了.显然,苹果认为如果为每一种拥有不同的被观察属性组合的对象创建将会导致花费激增.
另外,-class
方法也被重载了,为了隐藏动态子类的存在.
接下来,使用-methodForSelector
方法显示setX
方法的实现,两者的值是相同的.因为在动态子类中没有发现-methodForSelector
方法的重载,我们可以推测出-methodForSelector
方法使用了调用了-class
方法,从而产生这个结果.所以我们使用一个Objective-C运行时方法,得到了和使用上一个方法不同的结果.