Copy
OC中copy的作用是:利用一个源对象产生一个副本对象,它们之间不会相互影响。
关于深拷贝与浅拷贝
深拷贝是指对对象的具体内容进行复制,并占用新的内存空间,浅拷贝就是对内存地址的复制。
自定义的类如果要深拷贝,需要遵循 NSCopying, NSMutableCopying 协议,在协议方法中实现copy相关方法。
数组的深拷贝,也需要自己将所有对象拷贝一份再添加。
下面的代码:
NSMutableArray *array = [NSMutableArray array];
[array addObject:[NSObject new]];
[array addObject:[NSObject new]];
[array addObject:[NSObject new]];
[array addObject:[NSObject new]];
NSArray *copyArray = [array copy];
NSArray *copycopyArray = [copyArray copy];
NSMutableArray *mutACopy = [array mutableCopy];
NSMutableArray *arrCopy = [copyArray mutableCopy];
NSLog(@"原来的数组 :%p ----> %p",&array,*&array);
NSLog(@"copy的数组 :%p ----> %p",©Array,*©Array);
NSLog(@"copycopy的数组 :%p ----> %p",©copyArray,*©copyArray);
NSLog(@"mutableCopy原来的数组 :%p ----> %p",&mutACopy,*&mutACopy);
NSLog(@"mutableCopy copy的数组:%p ----> %p",&arrCopy,*&arrCopy);
输出的结果如下:
... Demo[11327:4606426] 原来的数组 :0x7ffee226d9a0 ----> 0x60400044a9e0
... Demo[11327:4606426] copy的数组 :0x7ffee226d998 ----> 0x60400044aa10
... Demo[11327:4606426] copycopy的数组 :0x7ffee226d990 ----> 0x60400044aa10
... Demo[11327:4606426] mutableCopy原来的数组 :0x7ffee226d988 ----> 0x60400044fcc0
... Demo[11327:4606426] mutableCopy copy的数组:0x7ffee226d980 ----> 0x60400044fcf0
打断点可以看到所有的Array中的元素的地址都是相同的,并没有进行复制,并从输出结果看出:
copy
的对象为MutableArray
时,会有一个新的指针指向新的内存地址(新的Array对象)。
copy
的对象为Array
时,会有一个新的指针指向原来的内存地址(原来的Array对象)。
mutableCopy
的对象为MutableArray
时,会有一个新的指针指向新的内存地址(新的MutableArray对象)。
mutableCopy
的对象为Array
时,会有一个新的指针指向新的内存地址(新的MutableArray对象)。
参考文章:
Objective-C中的浅拷贝和深拷贝
OC数组中的深拷贝
KVC
KVC(Key Valued Coding),键值编码,即常说的反射机制,是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性,进行属性的动态读写。
对于某些private属性,如果使用KVC进行修改,这就破坏了类的封装性(当然了有些情况不得不使用KVC进行修改)。
- KVC的主要用途是ORM映射,就是dictionary与model的互转。
常用方法
//获取值的方法
valueForKey: //传入NSString属性的名字。
valueForKeyPath: //传入NSString属性的路径,xx.xx形式。
valueForUndefinedKey //它的默认实现是抛出异常,可以重写这个函数做错误处理。
修改值的方法
setValue:forKey:
setValue:forKeyPath:
setValue:forUndefinedKey:
使用KVC实现ORM
假定有一个商品model,其定义属性如下:
@property (nonatomic,copy) NSString * goodsId; //商品ID
@property (nonatomic,copy) NSString * coverImage; //封面
@property (nonatomic,copy) NSString * shopPrice; //商城价(零售价)
@property (nonatomic,copy) NSString * sales; //销量
//...
服务器返回的字符串为:
{
"goods_id":"7",
"cover_image":"http://www.nenyimall.com/products/H6200IFLO.jpg",
"shop_price":"¥15.00",
"sales":"139",
...
}
其中有3个字段的key与我们定义的属性名有区别,这时候,只需重写下面两个方法:
-(void)setValue:(id)value forUndefinedKey:(NSString *)key{
if ([key isEqualToString:@"goods_id"]) {
[self setValue:value forKey:@"goodsId"];
}else
if ([key isEqualToString:@"cover_image"]) {
[self setValue:value forKey:@"coverImage"];
}else
if ([key isEqualToString:@"shop_price"]) {
[self setValue:value forKey:@"shopPrice"];
}
}
-(id)valueForUndefinedKey:(NSString *)key{
id result = nil;
if ([key isEqualToString:@"goods_id"]) {
result = [self valueForKey:@"goodsId"];
}else
if ([key isEqualToString:@"cover_image"]) {
result = [self valueForKey:@"coverImage"];
}else
if ([key isEqualToString:@"shop_price"]) {
result = [self valueForKey:@"shopPrice"];
}
return result;
}
然后就可以使用setValuesForKeysWithDictionary:将dictionary转换为model;
使用dictionaryWithValuesForKeys:将属性转换为dictionary。
参考文章:
iOS开发-OC篇-KVC详解
KVO
KVO(Key Valued Observer),键值观察,是使用获取其他对象的特定属性变化的通知机制。所有NSObject的子类都支持这个机制。
常用语法
/**
创建一个观察者
@param observer 观察者
@param keyPath 被观察的属性
@param options 传递给接收者的值的类型 NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
@param context 上下文,可用于区分注册者
*/
[被观察对象 addObserver:<#(nonnull NSObject *)#> forKeyPath:<#(nonnull NSString *)#> options:<#(NSKeyValueObservingOptions)#> context:<#(nullable void *)#>];
/**
观察者的回调方法
@param keyPath 被观察的属性
@param object 被观察的属性值
@param change 变化的记录
@param context 上下文
*/
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
//do something
}
/**
移除观察者
@param observer 观察者
@param keyPath 被观察的属性
*/
[被观察对象 removeObserver:<#(nonnull NSObject *)#> forKeyPath:<#(nonnull NSString *)#>];
局限性
父类和子类同时存在KVO时(监听同一个对象的同一个属性),很容易出现对同一个keyPath进行两次removeObserver操作,从而导致程序crash。要避免这个问题,就需要区分出KVO是self注册的,还是superClass注册的,我们可以在 -addObserver:forKeyPath:options:context:和-removeObserver:forKeyPath:context这两个方法中传入不同的context进行区分。
参考文章:
OC中KVO的基本概念和使用方法
iOS开发-KVO的奥秘
iOS下KVO使用过程中的陷阱