======文档更新状态=====
2015-12-27:发布
==================
day12-[2015-12-25]
一、KVC之KeyPath
1.当一个类具有嵌套对象属性时,需要使用KeyPath来给嵌套对象的属性直接赋值
2.语法:setValue:forKeyPath:
对应的get方法为:valueforKeyPath:
其中参数keyPath的写法为:使用.符号分隔的若干属性名称,如:horse.name、self.horse.speed
3.执行过程(以set为例):keyPath中最后一个分隔符.前面的都是通过valueForKey:进行查找,最后一个分隔符.后面的才通过setValue:forKey:赋值
// YYHero1.h
#import "YYHorse.h"
@interface YYHero: NSObject
@property (nonatomic,copy)NSString* name;
@property (nonatomic,strong)YYHorse* horse;
@end
// YYHero1.m
#import "YYHero.h"
@implementation YYHero
-(NSString *)description{
return [NSString stringWithFormat:@"Hero %@ has horse %@.",_name,_horse.name];
}
-(void)setValue:(id)value forUndefinedKey:(NSString *)key{
NSLog(@"set:%@ not founded.",key);
}
-(id)valueForUndefinedKey:(NSString *)key{
NSLog(@"get:%@ not founded.",key);
return nil;
}
@end
// YYHorse.h
#import <Foundation/Foundation.h>
@interface YYHorse : NSObject
@property (nonatomic,copy)NSString* name;
@property (nonatomic,assign)int speed;
@end
// YYHorse.m
#import "YYHorse.h"
@implementation YYHorse
-(NSString *)description{
return [NSString stringWithFormat:@"Horse %@'s speed is %d.",_name,_speed];
}
-(void)setValue:(id)value forUndefinedKey:(NSString *)key{
NSLog(@"horse set:%@ not founded.",key);
}
@end
// main.m
#import "YYHero.h"
int main0(int argc, const char * argv[]) {
@autoreleasepool {
YYHero* h=[YYHero new];
YYHorse* m=[YYHorse new];
//通过KVC给Horse赋值
[m setValue:@"Chitu" forKey:@"name"];
[m setValue:@100 forKey:@"speed"];
NSLog(@"m:%@",m);
//通过KVC给Hero赋值
[h setValue:@"Lvbu" forKey:@"name"];
[h setValue:m forKey:@"horse"];
NSLog(@"h:%@",h);
//通过KeyPath为嵌套对象赋值
YYHero* h1=[YYHero new];
[h1 setValue:@"Tang" forKey:@"name"];
[h1 setValue:[YYHorse new] forKey:@"horse"];
// 如果没有上面把马horse赋值给hero则下面的赋值无效但不会报错因为nil的存在
[h1 setValue:@"Bailong" forKeyPath:@"horse.name"];
[h1 setValue:@80 forKeyPath:@"horse.speed"];
// NSLog(@"hero.horse.name:%@",h.horse.name);
NSLog(@"h1:%@",h1);
NSLog(@"h1.horse:%@",h1.horse);
NSLog(@"h1.horse.name:%@",
[h1 valueForKeyPath:@"horse.name"]);
//执行过程:最后一个分隔符.前面的都是通过valueForKey进行查找,最后一个分隔符.后面的才通过setValue:forKey:赋值
[h1 setValue:@"Heilong" forKeyPath:@"horse.name"];
//可以使用keyPath为当前对象的属性赋值,实际意思是:self.属性名
[h1 setValue:@"Xuanzang" forKeyPath:@"self.name"];
NSLog(@"h1.name:%@",[h1 valueForKeyPath:@"name"]);
}
return 0;
}
//注意:
//1>[h1 setValue:[YYHorse new] forKey:@"horse"];这行
// 因为YYHero对YYHorse是强引用@property (nonatomic,strong)YYHorse* horse;
// 顾没有问题,但是如果上面的strong改为weak [YYHorse new]创建后就被销毁
//
//2>setValue:@"xxx"forKeyPath:aaa.bbb执行过程
//最后一个分隔符.前面的都是通过valueForKey进行查找(如果在当前类中没有找到就在当前类
//中报valueForUndefined错误),最后一个分隔符.后面的才通过setValue:forKey:赋值
//
//3>注意
//-(id)valueForUndefinedKey:(NSString *)key
//{
//
// NSLog(@"can't found key[%@]",key);
//
// return nil;
//
//}
//返回的nil
//当以这种形式:setValue:@"xxx"forKeyPath:aaa.bbb时
//访问aaa是以valueForKey的方式,当访问不到aaa时会调用valueForUndefinedKey:返回nil表示后面的都不在执行
//
二、KVO
1.观察者模式:在某个对象上针对某个或某几个属性添加观察者(addObserver:),当对象被观察的属性值发生变化,则观察者会执行相应方法(observeForKeyPath:……),在观察完成后。对象应及时移除(removeObserver:)观察者,否则销毁对象时会引起异常
2. addObserver :方法的options参数采用8421编码:1000 0100 0010 0001
1<<1 1<<2 1<<10
一般采用8421编码的枚举值都是可以使用多个值的,建议使用按位或|,使用+运算符也可以,但效率较低
3.注意事项:当重写setter:方法添加观察者时,需判断目标对象是否与参数对象相同,如果不同,需将原有对象的观察者移除才能添加,否则有可能造成销毁异常
// YYData.h
#import <Foundation/Foundation.h>
@interface YYData : NSObject
@property (nonatomic,copy)NSString* name;
@property (nonatomic,assign)int price;
@end
// YYData.m
#import "YYData.h"
@implementation YYData
-(void)dealloc{
NSLog(@"data %@ dealloc",_name);
}
@end
// YYView.h
#import "YYData.h"
@interface YYView : NSObject
@property (nonatomic,strong)YYData* data;
@end
// YYView.m
#import "YYView.h"
@implementation YYView
-(void)setData:(YYData *)data{
if (_data!=data) {
[_data removeObserver:self
forKeyPath:@"name"];
[_data removeObserver:self
forKeyPath:@"price"];
_data=data;
[_data addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];
[_data addObserver:self forKeyPath:@"price" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];
}
}
-(void)dealloc{
[_data removeObserver:self forKeyPath:@"name"];
[_data removeObserver:self
forKeyPath:@"price"];
NSLog(@"view dealloc");
}
/**
* 观察者观察到目标数据发生变化时执行的方法,必须由用户给出实现
*
* @param keyPath 受观察的目标的属性
* @param object 受观察的目标
* @param change 数据的变化,里面存放的内容由addObserver时的options参数决定
* @param context nil
*/
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
NSLog(@"keyPath:%@",keyPath);//name
NSLog(@"object:%@",[object class]);//YYData
NSLog(@"change:%@",change);//{old=xxx,new=xxx}
NSLog(@"new value:%@",[change valueForKey:@"new"]);
NSLog(@"old value:%@",[change valueForKey:@"old"]);
// 当添加多个属性监控时对每个属性监控的操作可能不一样
//如果是目标对象的price属性发生变化,输出变化前后的差值
if ([keyPath isEqual:@"price"]) {
int diff=[[change valueForKey:@"new"]intValue]-[[change valueForKey:@"old"]intValue];
NSLog(@"diff:%d",diff);
}
}
@end
// test_kvo2.m
#import "YYView.h"
int main(){
@autoreleasepool {
YYData* data1=[YYData new];
YYData* data2=[YYData new];
YYView* view=[YYView new];
data1.name=@"tom";
data2.name=@"mike";
view.data=data1;
data1.name=@"jerry";
NSLog(@"-----------");
data1.price=10;
NSLog(@"-----------");
view.data=data2;// 注意这里换了data,view类中setter的写法
data2.name=@"jack";
NSLog(@"-----------");
data2.price=20;
}
return 0;
}
//注意:
// 1> 在被观察者data被销毁之前要保证观察者view移除观察否则运行会出错(牢记!!)
// 所有在view中用strong还是weak引用data要遵循上面的规则,自己斟酌到底用哪个修饰符!!
// 修饰符会影响到代码的顺序:如果用strong无需特别关注代码的顺序,如果用weak要注意代码的顺序
//
// 2> 注意上面这句:view.data=data2;注意这里换了data,view类中setter的写法中加了判断,如果不换data则在
// setter中不需要加判断正是由于换了所以要加判断
//
// 3> 当添加多个属性监控时对每个属性监控的操作可能不一样
// 注意observeValueForKeyPath:ofObject: change: context: 方法中的操作写法
三、反射
1.获取Class,除了以前的类和对象方法class外,还可以通过库函数NSClassFromString(类名字符串)获取
2.通过获取到的类类型对象,可以用于创建该类实例,再通过KVC给对象实例赋值,整个过程无需导入该类的头文件
3.简单工厂模式,三大要素:父类产品、子类产品(可以多个),工厂类(包含一个创建产品的类方法)
// 简单工厂模式实现
// YYDrink.h
// 1225
#import <Foundation/Foundation.h>
/**
* 易拉罐饮料(父类产品)
*/
@interface YYDrink : NSObject
@property (nonatomic,copy)NSString* name;
@property (nonatomic,assign)int price;
@end
// YYDrink.m
#import "YYDrink.h"
@implementation YYDrink
-(NSString *)description{
return [NSString stringWithFormat:@"Drink %@'s price is %d.",_name,_price];
}
@end
// YYCole.h
#import "YYDrink.h"
/**
* 可乐(子类产品A)
*/
@interface YYCole : YYDrink
@end
// YYCole.m
#import "YYCole.h"
@implementation YYCole
@end
// YYSpirit.h
#import "YYDrink.h"
/**
* 雪碧(子类产品B)
*/
@interface YYSpirit : YYDrink
@end
// YYSpirit.m
#import "YYSpirit.h"
@implementation YYSpirit
@end
// YYFactory.h
#import "YYDrink.h"
/**
* 简单工厂类:用于生产YYDrink的子类产品
*/
@interface YYFactory : NSObject
+(YYDrink*)createDrink:(NSString*)type;
@end
// YYFactory.m
#import "YYFactory.h"
@implementation YYFactory
+(YYDrink *)createDrink:(NSString *)type{
Class clz=NSClassFromString(type);
id dnk=[clz new];
[dnk setValue:type forKey:@"name"];
[dnk setValue:@3 forKey:@"price"];
return dnk;
}
@end
// test_fac.m
#import "YYFactory.h"
#import "YYDrink.h"
int main0() {
//通过配置文件存储要创建的子类对象类型
//将指定文件中的内容按照指定编码格式存储为NSString对象
NSString* type=[NSString stringWithContentsOfFile:@"/Users/yangweibing/Desktop/drink.txt" encoding:NSUTF8StringEncoding error:nil];
YYDrink* d=[YYFactory createDrink:type];
NSLog(@"%@",d);
NSLog(@"class:%@",[d class]);//YYCole
return 0;
}
===========EOF===============