ios_obj-c KVC,谓词,KVO和通知

 http://blog.csdn.net/u_arenotalone

1.KVC

1.1.基本概念

1.1.1.KVC 是 KeyValue Coding 的简称,它是一种可以直接通过字符串的名字( key ) 来访问类属性的机制。

1.1.2.使用该机制不需要调用存取方法和变量实例就可访问对象属性。

1.1.3.本质上讲,键-值编码定义了你的程序存取方法需要实现的样式及方法签名。

1.1.4.在应用程序中实现键-值编码兼容性是一项重要的设计原则。存取方法可以加强合适的数据封装,而键-值编码方法在多数情况下可简化程序代码。

1.1.5.键-值 编码方法在 Objective-C 非标准协议(类目) NSKeyValueCoding 中 被声明,默认的实现方法由  NSObject 提供。

1.1.6.键-值编码支持带有对象值的属性,同时也支持纯数值类型和结构。非对象参数和返回类型会被识别并自动封装/解封。

1.1.7.使用 KVC 为对象赋值或者取值时,需要知道准确的键值, 相比较点语法,KVC 是一种间接的传递方式,这种方式有利于对象解耦,让对象彼此之间的耦合度不会太高。

1.2.设置和访问

1.2.1.键/值编码中的基本调用包括 -valueForKey: 和 -setValue:forkey: 这两个方法,它们以字符串的形式向对象发送消息,字符串为属性名,即键:

1
2
3
4
Person *jack = [[Person alloc] init];
NSMutableString *name = [[ NSMutableString alloc] initWithFormat: @"jack" ];
[jack setValue:name forKey: @"name" ]; //通过KVC设值
NSLog ( @"Jack's name : %@" , [jack valueForKey: @"name" ]); //通过KVC取值

1.2.2.是否存在 setter、getter 方法, 若存在优先调用相应方法;若不存在,它将在内部查找名为 _key 或 key 的实例变量。

1.2.3.通过 KVC 设置对象,此对象会 retain。

1.2.4.通过 setValue:forKey: 设置对象的值,或通过 valueForKey 来获取对象的值时,如若对象的实例变量为基本数据类型时 ( char、int、float、BOOL ) ,我们需要对数据进行封装。

1.2.5.赋值语句 setValue:forKey: 是给对象当前的属性赋值,而 setValue:forKeyPath: 是按照对象的层级关系为其中的属性赋值
。 forKeyPath可以替代forKey,但是forKey不能替代forKeyPath。

1.2.6.setValuesForKeysWithDictionary:  可以从 plist 文件中读取对应的数据字典,对对象属性赋值。

1.3.批处理

1
2
3
4
5
6
7
8
//同时获取 Student 的 age 和 name
NSArray *keys = [ NSArray arrayWithObjects: @"name" , @"age" , nil ];
NSDictionary *dict = [student dictionaryWithValuesForKeys:keys];
//同时设置 Student 的 age 和 name
NSArray *keys = [ NSArray arrayWithObjects: @"name" , @"age" , nil ];
NSArray *values = [ NSArray arrayWithObjects: @"MJ" , [ NSNumber numberWithInt:16], nil ];
NSDictionary *dict = [ NSDictionary dictionaryWithObjects:values forKeys:keys];
[student setValuesForKeysWithDictionary:dict];

1.4.路径

除了通过键设值或取值外, 键/值编码还支持指定路径设值或取值,像文件系统一样, 用“ . ”号隔开:

1
[book setValue: @"比尔" forKeyPath: @"author.name" ];
1
NSNumber *price=[book valueForKeyPath: @"relativeBooks.price" ]

1.4.数组的整体操作

如果向 NSArray 请求一个键值,它实际上会查询数组中的每个对象来查找这个键值, 然后将查询结果打包到另一个数组中并返回给你:

1
2
3
4
// 获取 Student 中所有 Book 的 name
NSArray *names = [student.books valueForKeyPath: @"name" ]; 或者
NSArray *names = [student valueForKeyPath: @"books.name" ];
//注意:不能在键路径中为数组添加索引,比如 @"books[0].name"

1.5.KVC的简单运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//count
NSString *count = [book valueForKeyPath: @"relativeBooks.@count" ];
NSLog ( @"count : %@" , count);
//sum
NSString *sum = [book valueForKeyPath: @"relativeBooks.@sum._price" ];
NSLog ( @"sum : %@" , sum);
//avg
NSString *avg = [book valueForKeyPath: @"relativeBooks.@avg._price" ];
NSLog ( @"avg : %@" , avg);
//min
NSString *min = [book valueForKeyPath: @"relativeBooks.@min._price" ];
NSLog ( @"min : %@" , min);
//max
NSString *max = [book valueForKeyPath: @"relativeBooks.@max._price" ];
NSLog ( @"max : %@" , max);

2.谓词

2.1.基本概念

cocoa 中提供了 NSPredicate 类,指定过滤器的条件。将符合条件的对象保留 下来。

2.2.创建谓词:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 设置谓词条件
NSPredicate *predicate = [ NSPredicate predicateWithFormat: @"age <= 28" ];
for (Person *person in array) {
     // 表⽰指定的对象是否满⾜谓词条件
     if ([predicate evaluateWithObject:person]) {
         NSLog ( @"person's name : %@" ,person.name);
     }
}
// 返回⼀个符合谓词条件的数组
NSArray *newArray = [array filteredArrayUsingPredicate: predicate];
for (Person *person in newArray) {
     NSLog ( @"person's name : %@" , [person valueForKey: @"_name" ]);
}

2.3.格式占位符

1
2
3
4
5
6
// 格式占位符号
NSPredicate *pre = [ NSPredicate predicateWithFormat: @" age <= %d" , 30];
NSArray *array2 = [array filteredArrayUsingPredicate:pre];
for (Person *person in array2) {
     NSLog ( @"person's name : %@" , [person valueForKey: @"_name" ]);
}

2.4.运算符

2.4.1.逻辑运算符

1
2
3
// 运算符号 && AND || OR
NSPredicate *pre = [ NSPredicate predicateWithFormat: @"name > 'bruse' && age < %d" , 30];
NSArray *array = [array filteredArrayUsingPredicate:pre];

2.4.2.IN

1
2
3
4
//注意字符串一定要添加''
NSPredicate *pre = [ NSPredicate predicateWithFormat: @"self.name IN {'rose', 'bruse'}" ]; //self.可以省略
NSArray *array = [array filteredArrayUsingPredicate:pre];
NSLog ( @"person's name : %@" , [array valueForKey: @"_name" ]);

2.4.3.以…开始: BEGINSWITH

1
2
3
4
// BEGINSWITH 检查某个字是否以...开头
NSPredicate *pre = [ NSPredicate predicateWithFormat: @"self.name BEGINSWITH 'J'" ];
NSArray *array = [array filteredArrayUsingPredicate:pre];
NSLog ( @"person's name : %@" , [array valueForKey: @"name" ]);

2.4.4.以…结束: ENDSWITH

1
2
3
4
// ENDSWITH 检查某个字符是以...结尾
NSPredicate *pre = [ NSPredicate predicateWithFormat: @"self.name endswith 'e'" ];
NSArray *array = [array filteredArrayUsingPredicate:pre];
NSLog ( @"person's name : %@" , [array valueForKey: @"name" ]);

2.4.5.包含: CONTAINS

1
2
3
4
// CONTAINS 检查包含某个字符
NSPredicate *pre = [ NSPredicate predicateWithFormat: @"self.name CONTAINS '⼩'" ];
NSArray *array = [array filteredArrayUsingPredicate:pre];
NSLog ( @"person's name : %@" , [array valueForKey: @"name" ]);

2.4.6.like

1
2
3
4
// like *:匹配任意多个字符 ?:表示一个字符 (正则)
NSPredicate *pre = [ NSPredicate predicateWithFormat: @"name like '?a*'" ];
NSArray *array = [array filteredArrayUsingPredicate:pre];
NSLog ( @"person's name : %@" , [array valueForKey: @"name" ]);

3.KVO

3.1.基本概念

3.1.1.Key Value Observing,直译为:基于键值的观察者。它提供一种机制,当 指定的对象的属性被修改后,则对象就会接受到通知。简单的说就是每次指定的被 观察的对象的属性被修改后,KVO就会自动通知相应的观察者了。

3.1.2.与 NSNotification不同,键-值观察中并没有所谓的中心对象来为所有观察者 提供变化通知。取而代之地,当有变化发生时,通知被直接发送至处于观察状态的 对象。NSObject提供这种基础的键-值观察实现方法。

3.1.3.你可以观察任意对象属性,包括简单属性,对一或是对多关系。对多关系的观 察者将会被告知发生变化的类型-也就是任意发生变化的对象。

3.1.4.键-值观察为所有对象提供自动观察兼容性。你可以通过禁用自动观察通知并实现手动通知来筛选通知。

3.1.5.不能观察已经被释放的对象,如果要观察,需要是强应用对象,或者被其他对象强应用的对象。

3.2.注册观察者

为了正确接收属性的变更通知,被观察者必须首先调用 addObserver:forKeyPath:options:context: 方法进行注册:

1
2
3
4
5
6
7
8
9
10
11
/* anObserver :监听器对象
  * keyPath :监听的属性
  * options :决定了当属性改变时,要传递什么数据给监听器
  */
-( void )addObserver:( NSObject *)anObserver forKeyPath:( NSString *)keyPath options:( NSKeyValueObservingOptions )options context:( void *)context
 
/* 使用NSKeyValueObservingOptionOld选项,可以将改变之前的值传递给观察者。(以变更字典中的一个项的形式)
  * 指定 NSKeyValueObservingOptionNew选项,可以将改变的新值传递给观察者。
  * 可以使用逐位“|”这两个常量,来指定同时传递上述两种类型的值。
  */
[_child addObserver: self forKeyPath: @"tired" options: NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context: nil ];

3.3.接受变更通知

当对象的一个被观察属性发生变动时,观察者收到一个 observeValueForKeyPath:ofObject:change:context:消息。所有观察者都必须 实现这一方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
  * keyPath : 键路径
  * object : 被观察者
  * change : 包含变更细节的字典
  * context : 注册观察者时提交的上下文指针,可以为任意类型的参数
  */
- ( void )observeValueForKeyPath:( NSString *)keyPath
                       ofObject:( id )object
                         change:( NSDictionary *)change
                        context:( void *)c{
     if ([keyPath isEqual: @"key" ]) {
         NSLog ( @"" );
     }
}
// observeValueForKeyPath 方法是 NSObject 的分类,意味着可以观察任何对象。

3.4.移除观察者

当观察者销毁时,或达到目的无需再使用 KVO 时,应该将观察者移除:

1
[_child removeObserver: self forKeyPath: @"key" ];

4.通知

4.1.与 KVO 不同:

4.1.1.自定触发通知,不像 KVO,属性值一经改变便触发通知。

4.1.2.回调方法自定,不像 KVO,需要重写一个方法。

4.1.3.观察者和被观察者都可以没有对方的引用,两者可以毫无关系。

4.2.监听通知

1
2
3
4
5
6
7
8
9
10
/*
  * self : 观察者对象为自身
  * @selector(notificationAction:) : 当收到通知时,调用notificationAction:方法
  * @"hapyValueNotification" : 监听通知名为@"hapyValueNotification"
  * nil : 传递参数为nil
  */
[[ NSNotificationCenter defaultCenter] addObserver: self
                                          selector: @selector (notificationAction:)
                                              name: @"hapyValueNotification"
                                            object: nil ];
1
2
3
4
5
6
7
8
//收到通知时的回调方法
- ( void )notificationAction:( NSNotification *)notification {
     Children *child = notification.object;
     [ self playWith:child];
}
- ( void )playWith:(Children *)child {
  child.hapyValue = 100;
};

4.3.发送通知

1
2
3
4
5
/*
  * @"hapyValueNotification" : 发送通知名为@"hapyValueNotification"
  * self : 传递参数为self,自身对象
  */
[[ NSNotificationCenter defaultCenter] postNotificationName: @"hapyValueNotification" object: self ];

4.4.移除通知

当观察者销毁时,或达到目的无需再使用 通知 时,应该将通知移除:

1
2
//移除当前对象上指定的通知,通知名:@"hapyValueNotification"
[[ NSNotificationCenter defaultCenter] removeObserver: self name: @"hapyValueNotification" object: nil ];
1
2
//移除当前对象上所有的通知
[[ NSNotificationCenter defaultCenter] removeObserver: self ];
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
该代码会出现编译错误。因为在类A_class中的name成员变量是一个字符串数组,而在成员函数put_name中使用了strcpy函数将一个字符串指针直接赋给了name数组,这是错误的。应该使用strcpy的安全版本strcpy_s,并指定要复制的字符串的长度。因此,需要将代码中的put_name函数修改为以下形式: ``` void put_name(string* s) { strcpy_s(name, sizeof(name), s->c_str()); } ``` 另外,由于在类A_class中的name成员变量是一个字符串数组,因此在show_name函数中应该使用cout输出name数组的内容,而不是直接输出name指针。因此,需要将代码中的show_name函数修改为以下形式: ``` void show_name() { cout << name << "\n"; } ``` 修改后的代码如下,运行结果会输出"Wang xiao hua"、"Chen ming"和"5555_12345678": ``` #include<iostream> #include<string> using namespace std; class A_class { string name[20]; public: void put_name(string* s) { strcpy_s(name, sizeof(name), s->c_str()); } void show_name() { cout << name << "\n"; } }; class B_class : public A_class { char phone_num[20]; public: void put_phone(char* num) { strcpy(phone_num, num); } void show_phone() { cout << phone_num << "\n"; } }; int main() { A_class* A_p; A_class A_obj; B_class B_obj; A_p = &A_obj; A_p->put_name(new string("Wang xiao hua")); A_p->show_name(); A_p = &B_obj; A_p->put_name(new string("Chen ming")); A_p->show_name(); B_obj.put_phone("5555_12345678"); ((B_class*)A_p)->show_phone(); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值