对KVC/KVO的一些总结

1.KVC

除了一般的赋值和取值的方法,我们还可以用Key-Value-Coding(KVC)键值编码来访问你要存取的类的属性。

A.Student类没有提供set方法,用kvc可以达到存取的目的。

 

@interface Student : NSObject
{
    NSString *name;
}
@end
  [student setValue:@"张三" forKey:@"name"];
  NSString *name = [student valueForKey:@"name"];

B.如果访问这个类里中的属性中的属性呢?那就用到了键路径.

@interface Course : NSObject
{
    NSString *CourseName;
}
@end

 

@interface Student : NSObject
{
    NSString *name;
    Course *course;
}
@end
        Student *student = [[Student alloc]init ];
        [student setValue:@"张三" forKey:@"name"];
        NSString *name = [student valueForKey:@"name"];
        NSLog(@"学生姓名:%@",name);
        
        Course *course = [[Course alloc]init];
        [course setValue:@"语文课" forKey:@"CourseName"];
        [student setValue:course forKey:@"course"];
        NSString *courseName = [student valueForKeyPath:@"course.CourseName"];
        NSLog(@"课程名称:%@", courseName);
        
        //也可以这样存值,存取时如果KeyPath不存在那么会崩溃。
        [student setValue:@"数学课" forKeyPath:@"course.CourseName"];
        courseName = [student valueForKeyPath:@"course.CourseName"];

 

C.自动封装基本数据类型

@interface Student : NSObject
{
    NSString *name;
    Course *course;
    NSInteger point;
}
[student setValue:@"88" forKeyPath:@"point"];
[NSString *point = [student valueForKey:@"point"];

类里面的point字段类型是NSInteger的但是set方法给的是NSString仍然可以正常存取。

D.操作集合

{
    NSString *name;
    Course *course;
    NSInteger point;
    NSArray *otherStudent;
}
        Student *student = [[[Student alloc]init ]autorelease];
        [student setValue:@"张三" forKey:@"name"];
        NSString *name = [student valueForKey:@"name"];
        NSLog(@"学生姓名:%@",name);
        
        [student setValue:@"88" forKey:@"point"];
        NSString *point = [student valueForKey:@"point"];
        NSLog(@"分数:%@", point);
        
        Student *student1 = [[[Student alloc]init]autorelease];
        Student *student2 = [[[Student alloc]init]autorelease];
        Student *student3 = [[[Student alloc]init]autorelease];
        [student1 setValue:@"65" forKey:@"point"];
        [student2 setValue:@"77" forKey:@"point"];
        [student3 setValue:@"99" forKey:@"point"];
        NSArray *array = [NSArray arrayWithObjects:student1,student2,student3,nil];
        [student setValue:array forKey:@"otherStudent"];
        NSLog(@"其他学生的成绩%@", [student valueForKeyPath:@"otherStudent.point"]);
        NSLog(@"共%@个学生", [student valueForKeyPath:@"otherStudent.@count"]);
        NSLog(@"最高成绩:%@", [student valueForKeyPath:@"otherStudent.@max.point"]);
        NSLog(@"最低成绩:%@", [student valueForKeyPath:@"otherStudent.@min.point"]);
        NSLog(@"平均成绩:%@", [student valueForKeyPath:@"otherStudent.@avg.point"]);
    }

有一些方法可以对集合进行相应操作。

2.KVO

key-value observing (KVO) 

观察 model 对象的变化

 

在 Cocoa 的模型-视图-控制器 (Model-view-controller)架构里,控制器负责让视图和模型同步。这一共有两步:当 model 对象改变的时候,视图应该随之改变以反映模型的变化;当用户和控制器交互的时候,模型也应该做出相应的改变。

 

.h文件 globalObject

@interface globalObject : NSObject {

    NSString * theNum;

}

+(globalObject *)sharedInstance;

//@property (nonatomic, assign) NSNumber theNum;

- (void)setTheNum1 :(NSNumber *)num;

- (void)setTheNum :(NSNumber *)num;

@end

.m 文件 globalObject



+(globalObject *)sharedInstance

{

    staticglobalObject *sharedglobalObjectInstance =nil;

    staticdispatch_once_t predicate;

    dispatch_once(&predicate, ^{

        sharedglobalObjectInstance = [[globalObjectalloc]init];

        

    });

    return sharedglobalObjectInstance;

}



- (void)setTheNum1 :(NSNumber *)num{

    [self willChangeValueForKey:@"theNum"];

    theNum = [numstringValue];

    [self didChangeValueForKey:@"theNum"];

}



- (void)setTheNum :(NSNumber *)num{

    theNum = [numstringValue];

}

+ (BOOL) automaticallyNotifiesObserversForKey:(NSString *)key {

    if ([keyisEqualToString:@"theNum"]) {

        returnNO;

    }

   return [superautomaticallyNotifiesObserversForKey:key];

}

XXXcontroller.m 



- (void)viewDidLoad {

    [superviewDidLoad];

    [[globalObjectsharedInstance]addObserver:selfforKeyPath:@"theNum"options:NSKeyValueObservingOptionNewcontext:nil];

    // Do any additional setup after loading the view from its nib.

}





- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {

    if ([keyPathisEqualToString:@"theNum"]) {

        id num = [changeobjectForKey:@"new"];

        NSInteger n = [((NSNumber *)num)integerValue];

        NSLog(@"%@----%ld",@"observeValueForKeyPath", n);

    }

}

- (IBAction)doSomething:(id)sender {

     staticNSInteger i =0;

  [[globalObject sharedInstance] setTheNum1:[[NSNumber alloc] initWithInteger:i++]];

  [[globalObject sharedInstance] setTheNum:[[NSNumber alloc] initWithInteger:i++]];

  [[globalObject sharedInstance]setValue:[[NSNumber alloc]initWithInteger:i++] forKey:@"theNum"];

    NSLog(@"-----%@",[[globalObject sharedInstance] valueForKey:(@"theNum")]);

}

使用KVO时遇到的问题

1.手动KVO

   要使用手动KVO时要重写automaticallyNotifiesObserversForKey这个方法,见上面代码,实现这个方法后,KVC方式或是属性seter,无法出发KVO,我们可以自己写个方法如setTheNum1手动发送消息实现KVO ,两个方法如下

willChangeValueForKey:@"theNum"];

didChangeValueForKey:@"theNum"];

现在只有这个方法可以触发KVO.

2.KVO的注意点。

   A.使用KVO的时候要注意线程安全,要保证收到通知后的方法 observeValueForKeyPath 在正确的线程执行,我觉得可以把这个通知的响应dispatch到具体线程执行。

   B.如果类成员变量已经声明为属性,那么self.xxx、[self  setXxx] 、可以触发KVO;_xxx = 无法触发KVO。

      如果类成员变量没有声明为属性,那么[self.setXxx]仍然可以触发KVO。

 

3.KVO的本质。

当某个类的对象第一次被观察时, 系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的 setter 方法。 
派生类在被重写的 setter 方法实现真正的通知机制 ,就如前面手动实现键值观察那样。这么做是基于设置属性会调用 setter 方法,而通过重写就获得了 KVO 需要的通知机制。当然前提是要通过遵循 KVO 的属性设置方式来变更属性值,如果仅是直接修改属性对应的成员变量,是无法实现 KVO 的。 
同时派生类还重写了 class 方法以“欺骗”外部调用者它就是起初的那个类。然后系统将这个对象的 isa 指针指向这个新诞生的派生类,因此这个对象就成为该派生类的对象了,因而在该对象上对 setter 的调用就会调用重写的 setter,从而激活键值通知机制。此外,派生类还重写了 dealloc 方法来释放资源。

 

- (void)showSelfMsg {

    NSLog(@"self->isa:%@", object_getClass([globalObject sharedInstance]) );

    NSLog(@"self class:%@",[[globalObject sharedInstance] class]);

    

    

    NSLog(@"self className:%s",object_getClassName([globalObject sharedInstance]));

    NSLog(@"self class-name:%s",object_getClassName([[globalObject sharedInstance] class]) );

}

 

在addObserver前后调用globalObject的showSelfMsg方法打印log如下。

 

 

2016-05-16 16:19:14.192 test[15938:949754] self->isa:globalObject

2016-05-16 16:19:14.193 test[15938:949754] self class:globalObject

2016-05-16 16:19:14.193 test[15938:949754] self className:globalObject

2016-05-16 16:19:14.193 test[15938:949754] self class-name:globalObject

 

2016-05-16 16:19:18.964 test[15938:949754] self->isa:NSKVONotifying_globalObject

2016-05-16 16:19:18.964 test[15938:949754] self class:globalObject  //重写了 class 方法以“欺骗”外部调用者它就是起初的那个类

2016-05-16 16:19:18.964 test[15938:949754] self className:NSKVONotifying_globalObject

2016-05-16 16:19:18.965 test[15938:949754] self class-name:globalObject//重写了 class 方法以“欺骗”外部调用者它就是起初的那个类

 

3.KVO的优缺点。

 

优点:

1.提供一个简单地方法来实现两个对象的同步

2.能够提供观察的属性的最新值和先前值

3.用keypaths 来观察属性,因此也可以观察嵌套对象

缺点:

1.观察的属性必须使用string来定义,因此编译器不会出现警告和检查

2.对属性的重构将导致观察不可用

3.复杂的“if”语句要求对象正在观察多个值,这是因为所有的观察都通过一个方法来指向

4.容易造成崩溃,比如多次删除kaypah监听,删除不存在监听,没实现监听方法,规避崩溃可以参考:

https://blog.csdn.net/klabcxy36897/article/details/51680423

暂时先总结这么多。如有问题麻烦大家指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值