1、键值编码的基本概念和用法
键值编码的基本概念
1. 键-值编码是一个用于间接访问对象属性的机制,使用该机制不需要调用存取方法和变量实例就可访问对象属性。
2. 键-值编码方法在Objective-C非正式协议(类目)NSKeyValueCoding中被声明,默认的实现方法由NSObject提供。
3. 键-值编码支持带有对象值的属性,同时也支持纯数值类型和结构。非对象参数和返回类型会被识别并自动封装/解封。
KVC的基本用法
设置和访问
键/值编码中的基本调用包括-valueForKey:和-setValue:forkey:这两个方法,它们以字符串的形式向对象发送消息,字符串是我们关注属性的关键。
Book *book = [[Book alloc] init];
NSMutableString *name = [[NSMutableString alloc] initWithFormat:@"jack"];
[book setValue:name forKey:@"name"];
NSLog(@"jack name : %@", [book valueForKey:@"name"]);
是否存在setter、getter方法,如果不存在,它将在内部查找名为_key或key的实例变量。
通过KVC,可以获取不存在getter方法的对象值,无需通过对象指针直接访问。
这里我们需要注意,当我们通过setValue:forKey:设置对象的值,或通过valueForKey来获取对象的值时,如若对象的实例变量为基本数据类型时
(char、int、float、BOOL),我们需要对数据进行封装。
路径与一对多的关系
路径
除了通过键设置值外,键/值编码还支持指定路径,像文件系统一样。用“点”号隔开。
[book setValue:@"比尔" forKeyPath:@"author._name"];
[book valueForKeyPath:@"realtiveBooks.price"]
一对多的关系
如果向NSArray请求一个键值,它实际上会查询数组中的每个对象来查找这个键值,然后将查询结果打包到另一个数组中并返回给你。
NSArray *booksArray = [NSArray arrayWithObjects:book1, book2, nil];
[book setValue:booksArray forKey:@"relativeBooks"];
NSLog(@"books 2: %@", [book valueForKeyPath:@"relativeBooks.price"]);
实现简单的运算
KVC的简单运算
此外,还可以应用一些字符做简单运算。sum、min、max、avg、count
NSString *count = [book valueForKeyPath:@"relativeBooks.@count"];
NSLog(@"count : %@", count);
NSString *sum = [book valueForKeyPath:@"relativeBooks.@sum._price"];
NSLog(@"sum : %@", sum);
NSString *avg = [book valueForKeyPath:@"relativeBooks.@avg._price"];
NSLog(@"avg : %@", avg);
NSString *min = [book valueForKeyPath:@"relativeBooks.@min._price"];
NSLog(@"min : %@", min);
NSString *max = [book valueForKeyPath:@"relativeBooks.@max._price"];
NSLog(@"max : %@", max);
示例:
打开xcode
点击 create a new xcode project
点击 左侧 Application 选择右侧中的 Command Line Tool(相当于windows下的command)
--- next
product Name:BookKVCDemo
Company identifier:com.imti
type: foundation 是一个framework
取消选择 use Automatic Reference Counting
--- next
--- create
new file...
name:Author
subclass: NSObject
Author.h
#import <Foundation/Foundation.h>
@interface Author : NSObject {
@private
NSString *_name;
}
@end
new file...
name:Book
subclass: NSObject
Book.h
#import <Foundation/Foundation.h>
#import "Author.h"
@interface Book : NSObject {
@private
NSString *_name;
Author *_author; //书的作者
NSArray *_relativeBooks; //相关的书籍
float price; //书的价格
}
@end
打开 main.m 加入
#import "Book.h"
在加入
@autoreleasepool {
Book *book = [[Book alloc] init];
Author *author = [[Author alloc] init];
[author setValue:@"jack" forKey:@"_name"];
[book setValue:[NSNumber numberWithFloat:12.6] forKey:@"price"];
NSNumber *price = [book valueForKeyPath:@"price"];
NSLog(@"%@",price);
//键值访问
[book setValue:@"ipad develper" forKey:@"name"];
[book setValue:author forKey:@"_author"];
}
运行
KVC的缺点,你感觉到了吗?
如果属性名字写错了 编译不会报错 但运行的时候会报错
加入
//路径访问
[book setValue:@"tom" forKeyPath:@"author.name"];
NSString *authorName = [book valueForKeyPath:@"_author._name"];
NSLog(@"%@",authorName);
运行
加入
//一对多的关系
NSMutableArray *releBooks = [NSMutableArray arrayWithCapacity:3];
for (int i=0; i<3; i++) {
Book *book = [[Book alloc] init];
NSString *name = [NSString stringWithFormat:@"job_%d",i];
[book setValue:name forKey:@"_name"];
[book setValue:@(12+i) forKey:@"price"];
[releBooks addObject:book];
[book release];
}
[book setValue:releBooks forKey:@"_relativeBooks"];
NSArray *names = [book valueForKeyPath:@"_relativeBooks._name"];
NSLog(@"%@",names);
运行
将
NSArray *names = [book valueForKeyPath:@"_relativeBooks._name"];
NSLog(@"%@",names);
注释掉加入
NSArray *names = [releBooks valueForKeyPath:@"_name"];
NSLog(@"%@",names);
运行
加入
//运算, 运算的字段必须是数值类型NSNumber或者基本数据类型,计算的结果是NSNumber
//运算关键字sum、min、max、avg、count
NSNumber *sum = [book valueForKeyPath:@"_relativeBooks.@avg.price"];
NSLog(@"sum: %@",sum);
运行
课堂练习1
定义一个Person对象,他叫乔布斯,他有许多苹果设备:
1个macbook,价格8000
1个iPhone, 价格5000
1个iPad, 价格3800
使用kvc为Person设置名字, 使用kvc计算乔布斯所有设备的总价。
示例:
打开xcode
点击 create a new xcode project
点击 左侧 Application 选择右侧中的 Command Line Tool(相当于windows下的command)
--- next
product Name:PersonKVC
Company identifier:com.imti
type: foundation 是一个framework
取消选择 use Automatic Reference Counting
--- next
--- create
new file...
name:Person
subclass: NSObject
Person.h
#import <Foundation/Foundation.h>
@interface Person : NSObject {
@private
NSString *name;
NSArray *apples;
}
@end
new file...
name:AppleDevice
subclass: NSObject
AppleDevice.h
#import <Foundation/Foundation.h>
@interface AppleDevice : NSObject {
@private
NSString *_name;
float _price;
}
@end
main.m
加入
#import "Person.h"
#import "AppleDevice.h"
在加入
@autoreleasepool {
AppleDevice *mac = [[AppleDevice alloc] init];
[mac setValue:@"macbook" forKey:@"_name"];
[mac setValue:@8000 forKey:@"_price"];
AppleDevice *iphone = [[AppleDevice alloc] init];
[iphone setValue:@"iphone" forKey:@"_name"];
[iphone setValue:@5000 forKey:@"_price"];
AppleDevice *ipad = [[AppleDevice alloc] init];
[ipad setValue:@"ipad" forKey:@"_name"];
[ipad setValue:@3800 forKey:@"_price"];
NSArray *apples = @[mac,iphone,ipad];
[ipad release];
[mac release];
[iphone release];
Person *p = [[Person alloc] init];
[p setValue:@"乔布斯" forKey:@"name"];
[p setValue:apples forKey:@"apples"];
NSNumber *sum = [p valueForKeyPath:@"apples.@sum._price"];
NSLog(@"%@",sum);
[p release];
}