Copy
OC中copy的作用是:利用一個源對象產(chǎn)生一個副本對象,它們之間不會相互影響陈醒。
關(guān)于深拷貝與淺拷貝
深拷貝是指對對象的具體內(nèi)容進(jìn)行復(fù)制惕橙,并占用新的內(nèi)存空間,淺拷貝就是對內(nèi)存地址的復(fù)制钉跷。
自定義的類如果要深拷貝弥鹦,需要遵循 NSCopying, NSMutableCopying 協(xié)議,在協(xié)議方法中實(shí)現(xiàn)copy相關(guān)方法爷辙。
數(shù)組的深拷貝彬坏,也需要自己將所有對象拷貝一份再添加。
下面的代碼:
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(@"原來的數(shù)組 :%p ----> %p",&array,*&array);
NSLog(@"copy的數(shù)組 :%p ----> %p",©Array,*©Array);
NSLog(@"copycopy的數(shù)組 :%p ----> %p",©copyArray,*©copyArray);
NSLog(@"mutableCopy原來的數(shù)組 :%p ----> %p",&mutACopy,*&mutACopy);
NSLog(@"mutableCopy copy的數(shù)組:%p ----> %p",&arrCopy,*&arrCopy);
輸出的結(jié)果如下:
... Demo[11327:4606426] 原來的數(shù)組 :0x7ffee226d9a0 ----> 0x60400044a9e0
... Demo[11327:4606426] copy的數(shù)組 :0x7ffee226d998 ----> 0x60400044aa10
... Demo[11327:4606426] copycopy的數(shù)組 :0x7ffee226d990 ----> 0x60400044aa10
... Demo[11327:4606426] mutableCopy原來的數(shù)組 :0x7ffee226d988 ----> 0x60400044fcc0
... Demo[11327:4606426] mutableCopy copy的數(shù)組:0x7ffee226d980 ----> 0x60400044fcf0
打斷點(diǎn)可以看到所有的Array中的元素的地址都是相同的膝晾,并沒有進(jìn)行復(fù)制栓始,并從輸出結(jié)果看出:
copy
的對象為MutableArray
時,會有一個新的指針指向新的內(nèi)存地址(新的Array對象)血当。
copy
的對象為Array
時幻赚,會有一個新的指針指向原來的內(nèi)存地址(原來的Array對象)。
mutableCopy
的對象為MutableArray
時臊旭,會有一個新的指針指向新的內(nèi)存地址(新的MutableArray對象)落恼。
mutableCopy
的對象為Array
時,會有一個新的指針指向新的內(nèi)存地址(新的MutableArray對象)离熏。
參考文章:
Objective-C中的淺拷貝和深拷貝
OC數(shù)組中的深拷貝
KVC
KVC(Key Valued Coding)佳谦,鍵值編碼,即常說的反射機(jī)制滋戳,是在運(yùn)行狀態(tài)中钻蔑,對于任意一個類啥刻,都能夠知道這個類的所有屬性和方法;對于任意一個對象矢棚,都能夠調(diào)用它的任意一個方法和屬性郑什,進(jìn)行屬性的動態(tài)讀寫府喳。
對于某些private屬性蒲肋,如果使用KVC進(jìn)行修改,這就破壞了類的封裝性(當(dāng)然了有些情況不得不使用KVC進(jìn)行修改)钝满。
- KVC的主要用途是ORM映射,就是dictionary與model的互轉(zhuǎn)兜粘。
常用方法
//獲取值的方法
valueForKey: //傳入NSString屬性的名字。
valueForKeyPath: //傳入NSString屬性的路徑弯蚜,xx.xx形式孔轴。
valueForUndefinedKey //它的默認(rèn)實(shí)現(xiàn)是拋出異常,可以重寫這個函數(shù)做錯誤處理碎捺。
修改值的方法
setValue:forKey:
setValue:forKeyPath:
setValue:forUndefinedKey:
使用KVC實(shí)現(xiàn)ORM
假定有一個商品model路鹰,其定義屬性如下:
@property (nonatomic,copy) NSString * goodsId; //商品ID
@property (nonatomic,copy) NSString * coverImage; //封面
@property (nonatomic,copy) NSString * shopPrice; //商城價(零售價)
@property (nonatomic,copy) NSString * sales; //銷量
//...
服務(wù)器返回的字符串為:
{
"goods_id":"7",
"cover_image":"http://www.nenyimall.com/products/H6200IFLO.jpg",
"shop_price":"¥15.00",
"sales":"139",
...
}
其中有3個字段的key與我們定義的屬性名有區(qū)別,這時候收厨,只需重寫下面兩個方法:
-(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轉(zhuǎn)換為model晋柱;
使用dictionaryWithValuesForKeys:將屬性轉(zhuǎn)換為dictionary。
參考文章:
iOS開發(fā)-OC篇-KVC詳解
KVO
KVO(Key Valued Observer)诵叁,鍵值觀察雁竞,是使用獲取其他對象的特定屬性變化的通知機(jī)制。所有NSObject的子類都支持這個機(jī)制拧额。
常用語法
/**
創(chuàng)建一個觀察者
@param observer 觀察者
@param keyPath 被觀察的屬性
@param options 傳遞給接收者的值的類型 NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
@param context 上下文碑诉,可用于區(qū)分注冊者
*/
[被觀察對象 addObserver:<#(nonnull NSObject *)#> forKeyPath:<#(nonnull NSString *)#> options:<#(NSKeyValueObservingOptions)#> context:<#(nullable void *)#>];
/**
觀察者的回調(diào)方法
@param keyPath 被觀察的屬性
@param object 被觀察的屬性值
@param change 變化的記錄
@param context 上下文
*/
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
//do something
}
/**
移除觀察者
@param observer 觀察者
@param keyPath 被觀察的屬性
*/
[被觀察對象 removeObserver:<#(nonnull NSObject *)#> forKeyPath:<#(nonnull NSString *)#>];
局限性
父類和子類同時存在KVO時(監(jiān)聽同一個對象的同一個屬性),很容易出現(xiàn)對同一個keyPath進(jìn)行兩次removeObserver操作侥锦,從而導(dǎo)致程序crash进栽。要避免這個問題,就需要區(qū)分出KVO是self注冊的恭垦,還是superClass注冊的快毛,我們可以在 -addObserver:forKeyPath:options:context:和-removeObserver:forKeyPath:context這兩個方法中傳入不同的context進(jìn)行區(qū)分。