Copy的作用
- 在OC中帐要,copy 是利用一個源對象產(chǎn)生一個副本對象,本質(zhì)就是當(dāng)修改源對象的屬性和行為弥奸,不會影響副本對象榨惠,同樣,當(dāng)修改副本對象的屬性和行為,不會影響源對象赠橙。
Copy 的使用
- 注意: 使用之前必須要遵守 NSCopying 協(xié)議耽装,實現(xiàn) copyWithZone: 方法
其中有兩點需要注意:
1、Foundation類已經(jīng)遵守了<NSCopying>和 <NSMutableCopying>協(xié)議,即實現(xiàn)了copy和mutableCopy方法,因此Foundation對象可以使用這些方法創(chuàng)建對象的副本或可變副本
期揪,例如: NSString 掉奄、 NSDictionary 、NSArray 凤薛、 NSMutableArr ...
2姓建、在iOS中并不是所有的對象都支持copy,mutableCopy缤苫,遵守NSCopying 協(xié)議的類可以發(fā)送copy消息速兔,遵守NSMutableCopying 協(xié)議的類才可以發(fā)送mutableCopy消息。假如發(fā)送了一個沒有遵守上訴兩協(xié)議而發(fā)送 copy或者 mutableCopy,那么就會發(fā)生異常榨馁。如果想自定義一下copy 那么就必須遵守 NSCopying,并且實現(xiàn) copyWithZone: 方法憨栽,如果想自定義一下mutableCopy 那么就必須遵守NSMutableCopying,并且實現(xiàn) mutableCopyWithZone: 方法, 總之想要 Copy 或是mutableCopy 必須要實現(xiàn) copyWithZone: 或是 mutableCopyWithZone: 方法 這兩個方法翼虫,協(xié)議不簽訂也沒有問題
屑柔,只不過不簽訂協(xié)議沒有協(xié)議方法的代碼提示.
例如: 自定義一個 Person 類,當(dāng)操作 copy 時珍剑,如果沒有實現(xiàn) NSCopying 協(xié)議掸宛,那么會出現(xiàn)錯誤。
Person *p1 = [[Person alloc]init];
Person *p2 = [p1 copy];
NSLog(@"P1 為:%@ P2 為: %@", p1, p2);
實現(xiàn)協(xié)議方法,輸出 P1 P2 對象地址
-(id)copyWithZone:(NSZone *)zone
{
Person *per = [[Person allocWithZone:zone]init];
return per;
}
深淺拷貝對比圖
源對象類型 | 拷貝方法 | 副本對象類型 | 是否產(chǎn)生新對象 | 拷貝類型 |
---|---|---|---|---|
NS* | copy | NS* | NO | 淺拷貝(指針拷貝 |
NS* | MutableCopy | NSMutable* | YES | 深拷貝(內(nèi)容拷貝) |
NSMutable* | copy | NS* | YES | 深拷貝(內(nèi)容拷貝) |
NSMutable* | MutableCopy | NSMutable* | YES | 深拷貝(內(nèi)容拷貝) |
自定義對象:
自定義對象不存在可變和不可變對象,所以均為深拷貝
深拷貝 淺拷貝
通過打印上述 P1 P2 對象别凤,我們發(fā)現(xiàn)饰序,內(nèi)存地址是不一樣的(深拷貝),這里我們引入深拷貝和淺拷貝的概念规哪。
淺拷貝
"淺拷貝可以簡單的理解為把對象的引用復(fù)制求豫,或者說對象的指針.指針賦值
,使兩個指針指向相同的一塊內(nèi)存空間诉稍,操作不安全蝠嘉。“
深拷貝
”深拷貝拷貝當(dāng)前指針指向的對象杯巨,系統(tǒng)會隨機給拷貝的對象重新分配一塊內(nèi)存蚤告,深拷貝以后,兩份對象的內(nèi)存地址不一樣服爷,指針指向也不一樣
杜恰。深拷貝會把當(dāng)前容器中的對象重新拷貝一份放到另一個容器中获诈,拷貝后的指針指向新的容器。不僅拷貝了對象還把指針頁拷貝了”
只有從不可變對象copy到不可變對象的時候才是淺拷貝箫章,其他的都是深拷貝
總結(jié)
1烙荷、對不可變的對象進行mutableCopy操作,是進行了一次深拷貝,返回的對象是可變的對象檬寂。
2终抽、對不可變的對象進行copy操作,進行了一次淺拷貝桶至,返回一個不可變的對象昼伴。
3、對可變得對象進行copy镣屹,進行了深拷貝圃郊,產(chǎn)生了不可變的對象副本。
4女蜈、對可變的對象進行了一次mutableCopy持舆,是進行了一次深拷貝, 返回的對象是一個可變的對象伪窖。
5逸寓、想要讓自定義的對象支持copy和mutableCopy那么就要對應(yīng)實現(xiàn)NSCopying協(xié)議,和NSMutableCopying協(xié)議覆山。
非容器類對象的copy和mutableCopy,以 NSString 為例
NSString *String = @"字符串";
self.aNewStr = [String copy];
self.twoNewStr =[String mutableCopy];
NSLog(@"1*******%p", String);
NSLog(@"2*******%p", _aNewStr);
NSLog(@"2*******%p", _twoNewStr);
結(jié)果輸出
2016-06-16 22:54:01.397 oc深淺拷貝[10541:843917] 1*******0x1099bd078
2016-06-16 22:54:01.397 oc深淺拷貝[10541:843917] 2*******0x1099bd078
2016-06-16 22:54:01.398 oc深淺拷貝[10541:843917] 2*******0x7fd959f1d820
通過打印結(jié)果我們可以看出:
- 通過 Copy 的字符串竹伸,不會產(chǎn)生新對象和String的內(nèi)存地址一樣,這里 copy 為淺拷貝(指針拷貝)
- 通過 mutableCopy 的字符串簇宽,產(chǎn)生的 twoNewStr 和原始的字符串 String 地址不一樣勋篓,這里產(chǎn)生了新的對象,為深拷貝(內(nèi)容拷貝)
@property 聲明中用 copy 修飾 NSString魏割、NSArray譬嚣、NSDictionary
因為父類指針可以指向子類對象,使用 copy 的目的是為了讓本對象的屬性不受外界影響,使用 copy 無論給我傳入是一個可變對象還是不可對象,我本身持有的就是一個不可變的副本.
如果我們使用是 strong ,那么這個屬性就有可能指向一個可變對象,如果這個可變對象在外部被修改了,那么會影響該屬性.
copy 此特質(zhì)所表達的所屬關(guān)系與 strong 類似。然而設(shè)置方法并不保留新值钞它,而是將其“拷貝” (copy)孤荣。 當(dāng)屬性類型為 NSString 時,經(jīng)常用此特質(zhì)來保護其封裝性须揣,因為傳遞給設(shè)置方法的新值有可能指向一個 NSMutableString 類的實例。這個類是 NSString 的子類钱豁,表示一種可修改其值的字符串耻卡,此時若是不拷貝字符串,那么設(shè)置完屬性之后牲尺,字符串的值就可能會在對象不知情的情況下遭人更改卵酪。所以幌蚊,這時就要拷貝一份“不可變” (immutable)的字符串,確保對象中的字符串值不會無意間變動溃卡。只要實現(xiàn)屬性所用的對象是“可變的” (mutable)溢豆,就應(yīng)該在設(shè)置新屬性值時拷貝一份。
NSMutableString *string = [NSMutableString stringWithString:@"origin"];//copy
NSString *stringCopy = [string copy];
查看內(nèi)存瘸羡,會發(fā)現(xiàn) string漩仙、stringCopy 內(nèi)存地址都不一樣,說明此時都是做內(nèi)容拷貝犹赖、深拷貝队他。即使你進行如下操作:
[string appendString:@"origion!"]
stringCopy 的值也不會因此改變,但是如果不使用 copy峻村,stringCopy 的值就會被改變麸折。 集合類對象以此類推。 所以粘昨,
用 @property 聲明 NSString垢啼、NSArray、NSDictionary 經(jīng)常使用 copy 關(guān)鍵字张肾,是因為他們有對應(yīng)的可變類型:NSMutableString芭析、NSMutableArray、NSMutableDictionary捌浩,他們之間可能進行賦值操作放刨,為確保對象中的字符串值不會無意間變動,應(yīng)該在設(shè)置新屬性值時拷貝一份尸饺。
retain 和 copy 的區(qū)別
1进统、retain是對當(dāng)前對象增加了一個指針指向,使對象的引用計數(shù)器加1浪听, 是進行了一次安全的淺拷貝操作螟碎。
2、copy是對當(dāng)前對象進行了一次拷貝迹栓,重新拷貝了當(dāng)前對象掉分,當(dāng)使用的時候減少了對當(dāng)前對象的依賴
當(dāng)產(chǎn)生子類時,copyWithZone: 方法需要注意的點
如果你的類產(chǎn)生了子類
,那么copyWithZone:方法也將被繼承克伊。
Student *stu = [[Student allocWithZone: zone] init];
該方法應(yīng)該改為:
Student *stu = [[[self class] allocWithZone: zone]init];
如果編寫一個類的copyWithZone:方法酥郭,那么子類的方法應(yīng)該先調(diào)用父類的copy方法以復(fù)制繼承來的copy實例變量.
//LIMING 繼承了 Student 在 copy 方法中,要先調(diào)用父類的 copy 方法愿吹, super
LIMING *copy = [super copyWithZone:zone];