KVO提供了一种方法,当某个属性改变时,相应的对象会被通知。
概述
1、通过runtime实现,当观察某个对象时,runtime会创建一个新的子对象。在这个新对象中,它重写了所有被观察的key,然后将object的isa指向新class(这个指针告诉OC运行时某个对象到底是哪种类型的对象)。因此这个添加观察者的对象就无声无息间变成了新的子类的实例。
2、当出发setKey方法时通知会发出。由于新子类这个方法被重写了,并且在内部添加了发送通知的机制。
3、苹果公司不希望将这个实现暴露在外面。于是重写了-class方法。这样依旧返回原先的class!如果不仔细看的话,是否被加入观察者的对象没什么区别。
代码
//
// main.m
// KVOTest
//
// Created by dujia on 16/2/4.
// Copyright © 2016年 杜甲. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import <objc/runtime.h>
@interface KVOTest1 : NSObject
@property (nonatomic , assign) int a;
@property (nonatomic , assign) int b;
@property (nonatomic , assign) int c;
@end
@implementation KVOTest1
@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, NSObject * obj)
{
NSString *str = [NSString stringWithFormat:
@"%@: %@\n\tNSObject class %s\n\tlibobjc class %s\n\timplements methods <%@>",
name,
obj,
class_getName([obj class]),
class_getName(object_getClass(obj)),
[ClassMethodNames(object_getClass(obj)) componentsJoinedByString:@", "]];
printf("%s\n", [str UTF8String]);
}
int main(int argc, char * argv[]) {
KVOTest1 *a = [[KVOTest1 alloc] init];
KVOTest1 *b = [[KVOTest1 alloc] init];
KVOTest1 *c = [[KVOTest1 alloc] init];
[c addObserver:c forKeyPath:@"c" options:0 context:NULL];
[b addObserver:b forKeyPath:@"b" options:0 context:NULL];
PrintDescription(@"a", a);
PrintDescription(@"b", b);
PrintDescription(@"c", c);
printf("Using NSObject methods, normal setB: is %p, overridden setB: is %p\n",
[a methodForSelector:@selector(setB:)],
[b methodForSelector:@selector(setB:)]);
printf("Using libobjc functions, normal setB: is %p, overridden setB: is %p\n",
method_getImplementation(class_getInstanceMethod(object_getClass(a), @selector(setB:))),
method_getImplementation(class_getInstanceMethod(object_getClass(b), @selector(setB:))));
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
运行结果:
a: <KVOTest1: 0x7f8b69f003f0>
NSObject class KVOTest1
libobjc class KVOTest1
implements methods <a, setA:, b, setB:, c, setC:>
b: <KVOTest1: 0x7f8b69f00460>
NSObject class KVOTest1
libobjc class NSKVONotifying_KVOTest1
implements methods <setB:, setC:, class, dealloc, _isKVOA>
c: <KVOTest1: 0x7f8b69f00480>
NSObject class KVOTest1
libobjc class NSKVONotifying_KVOTest1
implements methods <setB:, setC:, class, dealloc, _isKVOA>
Using NSObject methods, normal setB: is 0x10f4ff3b0, overridden setB: is 0x10f62ac6b
Using libobjc functions, normal setB: is 0x10f4ff3b0, overridden setB: is 0x10f62ac6b
没有加入观察者的A 输出: libobjc class KVOTest1