Copy的那點事兒~,Copy事兒~

來源:互聯網
上載者:User

Copy的那點事兒~,Copy事兒~
Copy的簡單使用copy 的效果

對來源物件進行拷貝,建立出新的副本,彼此修改互不干擾!

###OC中copy的方法

copy–>建立對象的副本

  • 如果對象有可變/不可變版本的區別,copy方法,只能拷貝出不可變的版本
  • 如果對象沒有可變/不可變的區別,copy方法就是建立一個副本

mutableCopy
* 建立對象的可變副本(如果對象有”可變/不可變”版本的區別,才需要使用此方法)

###副本的特點
彼此的內容一樣,具有相同的方法

可變版本對象的copy
/** 可變版本對象的copy    對於可變對象的copy/mutableCopy都是深拷貝 */- (void)copyDemo1 {    // arrayM本身為可變對象    NSMutableArray *arrayM = [NSMutableArray array];    NSLog(@"來源物件\t\t%@\t記憶體位址%p", arrayM.class, arrayM);    // copy ---> 不可變 地址變化 新的對象    // 無論是可變對象,還是不可以變對象,copy之後都會編程 不可變    id a = [arrayM copy];    NSLog(@"copy後\t%@\t記憶體位址%p", [a class], a);    // mutableCopy  => 可變 地址變化 新的對象,mutableCopy可以保持可變的特性    id aM = [arrayM mutableCopy];    NSLog(@"mutableCopy後%@\t記憶體位址%p", [aM class], aM);}

運行結果:

來源物件     __NSArrayM  記憶體位址0x7f91c2c1d940copy後   __NSArrayI  記憶體位址0x7f91c2e09a00mutableCopy後__NSArrayM  記憶體位址0x7f91c2d23660
不可變版本對象的copy
/** 不可變版本對象的copy */- (void)copyDemo2 {    NSArray *array = [NSArray array];    NSLog(@"來源物件%@ 記憶體位址%p", array.class, array);    // copy ---> 不可變 地址沒有變化 引用計數+1    // 對於不可變對象的copy操作,進行的淺拷貝,系統並不會為之分配記憶體空間,僅僅是retainCount+1    id a = [array copy];    NSLog(@"copy後%@ 記憶體位址%p", [a class], a);    // mutableCopy ---> 可變 地址變化 新的對象    // 不可變的mutaleCopy的操作會變為可變對象    id aM = [array mutableCopy];    NSLog(@"mutableCopy%@ 記憶體位址%p", [aM class], aM);}

執行結果:

 來源物件__NSArrayI 記憶體位址0x7fb4e07010d0 copy後__NSArrayI 記憶體位址0x7fb4e07010d0 mutableCopy__NSArrayM 記憶體位址0x7fb4e063daf0
小結:
  • 不要隨隨變變給可變對象做 copy 操作

  • 都會建立新的副本,深拷貝(只要有一個可以修改,就是深拷貝)

    可變  ----> 可變可變  ----> 不可變不可變 ----> 可變
  • 不會建立新的副本,只是引用計數+1,淺拷貝,指標拷貝(兩個對象前後都不需要修改)

    不可變 =》 不可變
Copy屬性

在物件導向程式開發中,有一個非常重要的原則

####開閉原則

-開:對內開放,向怎麼改,就怎麼改
-閉:對外封閉,只能用,不能改

  • 定義成 copy 屬性,在設定數值的時候,會預設做一次 copy 操作

    • 如果設定的數值是可變的,做一次copy,會建立副本
    • 如果設定的數值是不可變的,做一次copy,只是引用計數+1,不會建立新的副本!跟strong類型一致的!

建議:如果屬性是 NSString,建議使用 copy 屬性

注意:可變字串一定不要使用 copy 屬性

// 頭銜,如果區分可變和不可變版本,做一次copy操作得到的就是不可變的字串!@property (nonatomic, copy) NSMutableString *title;
對象的類型
  • 1> 一個對象的準確類型,是在給該對象”分配記憶體空間”的時候指定的類型
  • 2> 對象的”類型”,是程式員指定該對象的類型,指定類型之後,就可以具有該對象的方法!
  • 3> 能否使用對象的方法,取決於運行時,這個對象的類型是否真的正確!
  • 4> 如果類型不正確會出現 -[NSObject length]: unrecognized selector
NSMutableString *strM = [NSMutableString stringWithString:@"zhangsan"];    Person *p = [[Person alloc] init];    p.name = strM;    NSLog(@"%@", p.name);  // zhangsan    [strM setString:@"lisi"];    NSLog(@"===> %@", p.name);  // zhangsan    // 問題:p.name的類型 NSString & NSMutableString    // 答案:NSCFString --->  NSString    // NSMutableString類型的資料做了一次copy後,會變為不可變的NSString類型    id obj = p.name;    NSLog(@"%@", [p.name class]);  // NSCFString//    [obj setString:@"wangwu"];  // 報錯,不應該對NSString進行修改    NSLog(@"===> %@", p.name);  // wangwu
  NSMutableString *strM = [NSMutableString stringWithString:@"zhangsan"];    NSString *str = @"haha";    NSLog(@"%p", str);  // 0x10a182138    Person *p = [[Person alloc] init];//        p.name = strM;    p.name = str;    NSLog(@"%p", p.name); //0x10a182138  跟 str地址是一樣滴~,p.name指向了str的空間    NSLog(@"%@", p.name); // haha    [strM setString:@"lisi"];    NSLog(@"===> %@ %@", p.name, strM);  // p.name 和 strM不可能一樣,strM的改變不會影響到p.name
  // 從網路擷取到一個字串    NSMutableString *strM = [NSMutableString stringWithString:@"BOSS"];    Person *p = [[Person alloc] init];    p.title = strM;    [strM setString:@"經理"];    NSLog(@"===> %@ %@", p.title, strM);    // Attempt to mutate immutable object with setString:    // 試圖使用 setString: 方法修改"不可變對象"?    // setString方法,是title存在之後,修改title的內容!    [p.title setString:@"jingli"];  // 程式會崩掉,p.title為不可變對象    NSLog(@"!!!!> %@ %@", p.title, strM);
Copy的自訂對象
  • 在很多商業級應用程式或者第三方架構,在開發時的模型通常會支援 copy

    NSCache & NSMutableDictionary

    -NSCache 的 key strong 的-Dict 的 key 是 copy 的

    // NSMutableDictionary *dictM = [NSMutableDictionary dictionary];
    // dictM setObject:<#(id)#> forKey:<#(id<NSCopying>)#>

  • 如果自訂對象要當作字典的 key,需要支援 copy!

Person.h
/** 要讓自訂對象支援 copy,需要做兩件事情 1. 遵守 NSCopying 協議 2. 實現 copyWithZone: 方法 */@interface Person : NSObject <NSCopying>@property (nonatomic, copy) NSString *name;@property (nonatomic, assign) int age;
Person.m
/** 有傳回值 -》 copy 出來的新對象 是一個對象方法 -> 將 self 建立一個副本 zone: 空間,指派至是需要記憶體空間的,如果指定了zone,就可以指定建立對象對應的記憶體空間 但是:zone是一個非常古老的技術,為了避免在堆中出現記憶體片段而使用的 在今天的開發中,zone幾乎可以忽略 如果對象沒有 可變/不可變 的版本區別,只要實現 copyWithZone 方法即可 */- (id)copyWithZone:(NSZone *)zone {    // copy 是要建立一個新的副本,和當前的對象具有相同的內容    // 1. 執行個體化 person 對象    Person *p = [[Person alloc] init];    p.name = self.name;    p.age = self.age;    return p;}// 只需要寫模型的description就可以了,返回對象的描述資訊,便於調試使用,類似於 java 中的 toString()- (NSString *)description {    // JSON的格式和字典非常像//    @{@"name": @"zhangsan", @"age": @(19)}    return [NSString stringWithFormat:@"<%@: %p> {name: %@, age: %d}", self.class, self, self.name,self.age];}// 也可以輸出調試資訊的字串,專門用來調試使用的// 有的網站上的培訓資料會提到這個方法,跟 description 方法非常類似!// 但是:如果在應用程式中,使用了這個方法,應用程式無法上架!蘋果會認為使用了私人API//- (NSString *)debugDescription {////}
- (void)viewDidLoad {    [super viewDidLoad];    Person *p = [[Person alloc] init];    p.name = @"zhangsan";    p.age = 19;    NSLog(@"%@--- %p", p.name, p); // zhangsan--- 0x7fbacbf17120    Person *p1 = [p copy];    p1.name = @"xiaofang";    // 注意,地址不同,說明實現了自訂對象的copy    NSLog(@"%@--- %p", p1.name, p1); // xiaofang--- 0x7fbacbe13900}
子類對象的Copy
#import "Person.h"@interface Student : Person// 學號@property (nonatomic, copy) NSString *no;@end```objc<div class="se-preview-section-delimiter"></div>#import "Student.h"@implementation Student- (id)copyWithZone:(NSZone *)zone {    // 執行父類的 copy 方法,會把父類中的屬性完全 copy    Student *s = [super copyWithZone:zone];    // 在子類的copy方法中,只需要給子類特有的屬性進行賦值即可!    s.no = self.no;    return s;}@end<div class="se-preview-section-delimiter"></div>

在父類的copyWithZone方法中:
要寫:

    Person *p = [[self.class alloc] init];    p.name = self.name;    p.age = self.age;<div class="se-preview-section-delimiter"></div>

不能寫 Person * p = [[Person alloc] init];這麼幹了,只能copy出Person對象,不對子類起作用。

測試Demo
// 一個對象的準確類型,取決分配記憶體空間指定的類型    Person *p = [[Student alloc] init];    p.name = @"zhangsan";    p.age = 19;    // 給對象指定的類型,決定了能夠使用對象的哪些屬性和方法//    p.no = @"001";//    NSLog(@"%@ %@", p, p.no);    NSLog(@"%@", p); // {name: zhangsan, age: 19}    // copy會執行父類的copy方法    Student *p1 = [p copy];  // 雖然是父類的引用,但實際上copy的是子類    p1.name = @"xiaofang";    NSLog(@"%@ %@", p1, p1.no); // <Student: 0x7fb0f9443150> {name: xiaofang, age: 19} (null)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.