码迷,mamicode.com
首页 > 其他好文 > 详细

关于KVO的技巧

时间:2015-05-07 12:04:58      阅读:143      评论:0      收藏:0      [点我收藏+]

标签:

1、在父类中注册KVO 子类中KVO不相应的解决办法 在父类中通过判断发送对象的类是不是父类或者子类

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    //区分子类和父类KVO 简单判断
    if (object == self) {
        if ([keyPath isEqualToString:@"x"]) {
            NSObject *new = [change objectForKey:@"new"];
            NSLog(@"new x is %@",new);
        } else {
            [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
        }
    }
    else {
     //调用子类 [object observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } }

2、不要盲目的removeObserver,特别是父类和子类中同时存在KVO 因为容易出现二次removeObserver 导致程序崩溃。

3、KVO底层的实现原理

@interface ClassTest : NSObject
{
    int x;
    int y;
    int z;
}

@property (nonatomic, assign) int x;
@property (nonatomic, assign) int y;
@property (nonatomic, assign) int z;
@end
@implementation ClassTest
@synthesize x, y,z;

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if([keyPath isEqualToString:@"x"])
    {
        NSObject* new = [change objectForKey:@"new"];
        NSLog(@"new x is %@", new);
    }
    else
    {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}
@end
//获取方法列表
static NSArray* classMethodList(Class c) { NSMutableArray* array = [NSMutableArray arrayWithCapacity:5]; unsigned int count = 0; Method* methodList = class_copyMethodList(c, &count); for(int i = 0; i < count; ++i) { SEL sel = method_getName(*(methodList+i)); [array addObject:NSStringFromSelector(sel)]; } free(methodList); return array; }
static void printDescription(NSString* name, id obj)
{
    NSString* string = [NSString stringWithFormat:@"%@:%@\n\tclass %@\n\tobjclass %@\n\timplementmethod %@\n",
                        name,
                        obj,
                        [obj class],
                        object_getClass(obj),
                        [classMethodList(object_getClass(obj)) componentsJoinedByString:@" , "]];
    printf("%s", [string UTF8String]);
}

  

     ClassTest* x = [[ClassTest alloc] init];
        ClassTest* y = [[ClassTest alloc] init];
        ClassTest* xy = [[ClassTest alloc] init];
        ClassTest* control = [[ClassTest alloc] init];
        
        [x addObserver:x forKeyPath:@"x" options:NSKeyValueObservingOptionNew context:nil];
        [y addObserver:y forKeyPath:@"y" options:NSKeyValueObservingOptionNew context:nil];
        [xy addObserver:xy forKeyPath:@"x" options:NSKeyValueObservingOptionNew context:nil];
        [xy addObserver:xy forKeyPath:@"y" options:NSKeyValueObservingOptionNew context:nil];
        
        printDescription(@"x", x);
        printDescription(@"y", y);
        printDescription(@"xy", xy);
        printDescription(@"control", control);
        
        printf("Using NSObject method, normal setX is %p, overrite setX is %p\n", [control methodForSelector:@selector(setX:)], [x methodForSelector:@selector(setX:)]);
        printf("Using libobjc method, normal setX is %p, overrite setX is %p\n",
               class_getMethodImplementation(object_getClass(control), @selector(setX:)),
               class_getMethodImplementation(object_getClass(x), @selector(setX:)));

  输出结果:

      

x:<ClassTest: 0x8951690>
	class ClassTest
	objclass NSKVONotifying_ClassTest
	implementmethod setY: , setX: , class , dealloc , _isKVOA
y:<ClassTest: 0x89516c0>
	class ClassTest
	objclass NSKVONotifying_ClassTest
	implementmethod setY: , setX: , class , dealloc , _isKVOA
xy:<ClassTest: 0x89516d0>
	class ClassTest
	objclass NSKVONotifying_ClassTest
	implementmethod setY: , setX: , class , dealloc , _isKVOA
control:<ClassTest: 0x89516e0>
	class ClassTest
	objclass ClassTest
	implementmethod z , x , setX: , y , setY: , setZ: , observeValueForKeyPath:ofObject:change:context:
Using NSObject method, normal setX is 0x4ae0, overrite setX is 0x1134526
Using libobjc method, normal setX is 0x4ae0, overrite setX is 0x1134526

  实际上系统定了一个叫做 NSKVONotifying_ClassTest的子类,子类中实现了

      setY: , setX: , class , dealloc , _isKVOA函数,这个_isKVOA函数应该是个私有函数,用来判断是否kvo框架生成的类,x, y, xy对象的运行时类都指向NSKVONotifying_ClassTest,通过class函数返回的类还是指向ClassTest,但是control对象的不管运行时类还是class函数返回的类都指向ClassTest。这样就验证了系统是通过定义Classtest类的子类来实现属性方法发送通知的,系统很聪明,子类中并没有实现setZ方法,因为我们并没有对属性z添加观察者。

      在看看最后两行打印的结果,control对象的setX函数地址和x对象的setX函数地址是不一样的,说明setX函数被重写了。看别人之前的文章,通过NSObject方法打印control和x的setX函数地址是一样的,现在验证的结果地址却不一样,和使用runtime方法打印的结果完全一致,这个估计是新的系统底层做了修改,让使用NSObject的methodForSelector方法获得函数是子类的函数。

  

 

关于KVO的技巧

标签:

原文地址:http://www.cnblogs.com/wydoublefish/p/4484080.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!