一. 實現(xiàn)原理
- 動態(tài)生成子類 :
NSKVONotifying_xxx
1.1 中間生成的是一個動態(tài)類,但是修改的是 原對象的isa
- 觀察的是
setter
- 動態(tài)子類重寫了很多方法
setter
class
dealloc
_isKVOA
- 移除觀察的時候 isa 指向回來
- 動態(tài)子類不會銷毀
@interface FXPerson : NSObject
@property (nonatomic, copy) NSString *name;
- (void)test001;
- (void)test002;
@end
@interface FXViewController ()
@property (nonatomic, strong)FXPerson *person;
@end
@implementation FXViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.person = [[FXPerson alloc] init];
[self printClasses:[FXPerson class]];
[self printClassAllMethod:[FXPerson class]];
[self.person addObserver:self forKeyPath:@"name" options:(NSKeyValueObservingOptionNew) context:NULL];
[self printClasses:[LGPerson class]];
[self printClassAllMethod:NSClassFromString(@"NSKVONotifying_FXPerson")];
}
#pragma mark - 遍歷類以及所有子類
- (void)printClasses:(Class)cls{
// 注冊類的總數(shù)
int count = objc_getClassList(NULL, 0);
// 創(chuàng)建一個數(shù)組砚亭, 其中包含給定對象
NSMutableArray *mArray = [NSMutableArray arrayWithObject:cls];
// 獲取所有已注冊的類
Class* classes = (Class*)malloc(sizeof(Class)*count);
objc_getClassList(classes, count);
for (int i = 0; i<count; i++) {
if (cls == class_getSuperclass(classes[i])) {
[mArray addObject:classes[i]];
}
}
free(classes);
NSLog(@"classes = %@", mArray);
}
#pragma mark - 遍歷cls所有的方法
- (void)printClassAllMethod:(Class)cls{
unsigned int count = 0;
Method *methodList = class_copyMethodList(cls, &count);
for (int i = 0; i<count; i++) {
Method method = methodList[i];
SEL sel = method_getName(method);
IMP imp = class_getMethodImplementation(cls, sel);
NSLog(@"%@-%p",NSStringFromSelector(sel),imp);
}
free(methodList);
}
@end
在添加
[self.person addObserver:self forKeyPath:@"name" options:(NSKeyValueObservingOptionNew) context:NULL]
之前, 方法- (void)printClasses:(Class)cls
只輸出一個FXPerson
類, 而- (void)printClassAllMethod:(Class)cls
輸出test001-0x10992c560
test002-0x10992c570
. 當(dāng)添加觀察者方法之后,
- (void)printClasses:(Class)cls
輸出兩個類
FXPerson
,NSKVONotifying_FXPerson
- (void)printClassAllMethod:(Class)cls
輸出四個方法
setName:-0x7fff258e454b
class-0x7fff258e2fd5
dealloc-0x7fff258e2d3a
_isKVOA-0x7fff258e2d32
KVO 會為需要observed的對象動態(tài)創(chuàng)建一個子類NSKVONotifying_xxx
宠蚂,然后將原對象的 isa
指針指向新的子類魂奥,同時重寫class
方法,返回原先類對象戳表,這樣外部就無感知了;其次重寫所有要觀察屬性的setter
方法昼伴,統(tǒng)一會走一個方法匾旭,然后內(nèi)部是會調(diào)用 willChangeValueForKey
和 didChangevlueForKey
方法,在一個被觀察屬性發(fā)生改變之前圃郊, willChangeValueForKey:
一定會被調(diào)用价涝,這就 會記錄舊的值。而當(dāng)改變發(fā)生后持舆,didChangeValueForKey:
會被調(diào)用色瘩,繼而 observeValueForKey:ofObject:change:context:
也會被調(diào)用。在移除觀察的時候又將isa指回原對象, 移除的時候這個中間動態(tài)子類不會銷毀.