最简单的想法是我们创建一个对象,然后利用某些方法给这个对象赋值,然后再用某些方法调用这个值。当然这种描述很类似于字典的键值概念,确实今天学习的就是这样一种方法:KVC,Key-Value coding键值编码。
(1)简单的KVC
创建一个Book类,在Book.h里写一个私有的实例变量name,然后再main.m里创建一个book1对象,再给这个book1对象的私有变量name赋值,然后再调用这个值。神奇的地方就在这里:这种方法根本不管这个实力变量是private还是public。呃,Book.m里没有写入任何东西。
//Book.h文件
#import <Foundation/Foundation.h>
@interface Book : NSObject{
@private
NSString *name;
}
@end
//main.m文件
#import <Foundation/Foundation.h>
#import "Book.h"
int main(int argc, const char * argv[])
{
@autoreleasepool {
Book * book1=[[Book alloc]init];
//给对象的实例变量赋值,用setValue,核心是这里的key要么是实例变量同名,要么在前面加一个下划线_,否则报错
[book1 setValue:@"jack" forKey:@"name"];
//调用时候的key同上,要么是实例变量同名,要么加下划线_
NSString *name1=[book1 valueForKey:@"name"];
NSLog(@"%@",name1);
}
return 0;
}
结果当然是jack。
提示:
a:设定的key要么与实例变量同名,要么前面加_,因为程序运行时会去类中找,先找同名的,再找带有下划线_的,找不到就报错。
b:我们用NSString *name来做实例变量,name是一个字符串对象,没问题。但不能用直接的数据类型,需要对数据类型进行封装成数据对象NSNumber。
c:采用这种方法可以说十分强大,就算实例变量没有setter和getter方法,依然没问题。
d:缺点在于,采用这种方法编写的时候不会报错,在运行的时候会直接崩溃。
(2)复杂一些的带有路径链条访问的例子
创建一个Author类,只有一个私有变量aName,并且在Book类中引入这个对象作为对象变量author。这样Book类的对象就可以通过访问自身的author对象,再访问author对象的aName,实现依靠路径的访问。
//Author.h文件
#import <Foundation/Foundation.h>
@interface Author : NSObject{
@private
NSString *aName;
}
@end
//Book.h文件
#import <Foundation/Foundation.h>
#import "Author.h"//别忘了引入这个类
@interface Book : NSObject{
@private
NSString *name;
Author *author;//增加一个对象变量
}
@end
//main.m文件
#import <Foundation/Foundation.h>
#import "Book.h"
int main(int argc, const char * argv[])
{
@autoreleasepool {
Book * book1=[[Book alloc]init];
Author *author1=[[Author alloc]init];
//把author对象当做值赋给book1里地对象变量,把jack赋值给author对象里面的aName
[author1 setValue:@"jack" forKey:@"aName"];
[book1 setValue:author1 forKey:@"author"];
//通过路径调用,book1的author的aName
NSString *aName1=[book1 valueForKeyPath:@"author.aName"];
NSLog(@"%@",aName1);
}
return 0;
}
结果当然是jack。
(3)一对多的访问
就是一个数组里面装3个对象,每个对象有一对键值,但是三个对象的键相同,所以我们访问的时候,满足条件的键对应的值都会被调出来。
NSArray *relativeBooks;//在Book.h里增加数组变量
//main.m文件
#import <Foundation/Foundation.h>
#import "Book.h"
int main(int argc, const char * argv[])
{
@autoreleasepool {
//创建一个数组
NSMutableArray *mutArr1=[NSMutableArray arrayWithCapacity:3];
//往数组里面填充三个对象,三个对象有在相同的键名就是name
for (int i=0; i<3; i++) {
Book *book1=[[Book alloc]init];
NSString *name1=[NSString stringWithFormat:@"book_%d",i];
[book1 setValue:name1 forKey:@"name"];
[mutArr1 addObject:book1];
}
//再创建一个新对象,把这个数组赋值给这个对象的数组变量
Book * book2=[[Book alloc]init];
[book2 setValue:mutArr1 forKey:@"relativeBooks"];
//然后再来通过路径访问,而且访问数据里面的键时会把满足条件的都调出来组成新的数组返回
NSArray *arr2=[book2 valueForKeyPath:@"relativeBooks.name"];
NSLog(@"%@",arr2);
}
return 0;
}
结果:
(
"book_0",
"book_1",
"book_2"
)
其实,针对上面的还可以进行简单地运算:即如果上面数组要访问的不是书名,而是价格price,那么可以在访问的同时做一些求和、最大、最小的运算。修改如下:
a:在Book.h中继续增加一个float price的实例变量;
b:在循环语句赋值中[book1 setValue:@(12+i) forKey:@"price"];记得在数字前面加@,要把它们变成对象才可以;
c:在最后调用的时候,用NSNUmber *num=[book2 valueForKeyPath:@"relativeBooks.@sum.price"];其中@sum就是加入的运算,也可以是@min和@max和@avg等。