iOS KVO 實現分析
阿新 • • 發佈:2018-11-01
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