在OC語法中,提供了Copy語法(Copy + MutableCopy)用於對象的拷貝。其中很容易混淆的是淺拷貝和深拷貝。
所謂淺拷貝,即是地址拷貝,並不產生新的對象,而是對原對象的引用計數值加1。而深拷貝,即是對象拷貝,產生新的對象副本,計數器為1。
下面通過一個例子來分析一下這個比較容易亂的Copy:
一、對於NSString/NSMutableString; NSArray/NSMutableArray... 這OC提供的類對象:
以NSString/NSMutableString為例:
對於copy,返回的一定是不可變類型;而mutableCopy,返回的一定是可變類型。
①對於 mutableCopy ,一定是深拷貝。
//對於 mutableCopy,都是深拷貝:對象的拷貝,產生新的對象 void strMutableCopy(){ NSString *str=[[NSString alloc]initWithFormat:@"abcd"]; // NSMutableString *str = [[NSMutableString alloc] initWithFormat:@"abcd"]; //產生了一個新的對象 計數器為1 源對象的計數器不變 NSMutableString *str2=[str mutableCopy]; // NSString *str2 = [str mutableCopy]; //輸出二者的地址,二者的地址是不同的 NSLog(@"str --> %p",str); NSLog(@"str2 --> %p",str2); }
②對於 copy:
如果是 NSString ---> NSString;則是淺拷貝;如果是 NSMutableString ---> NSString;則是 深拷貝。
如果是 NSString 、NSMutableString ---> NSMutableString;則是深拷貝。
注:只有一種情況下是發生淺拷貝:不可變對象 復制到 不可變對象。
//淺拷貝:指針拷貝 不會產生新的對象,源對象計數器加1 void strCopy(){ NSString *str=[[NSString alloc]initWithFormat:@"abcd"]; //因為NSString對象本身就不可變,所以並沒產生新的對象,而是返回對象本身,會做一次retain操作,所以源對象也會retain NSString *str2=[str copy]; //輸出二者的地址,二者的地址是相同的 NSLog(@"str --> %p",str); NSLog(@"str2 --> %p",str2); }
例如:
//深拷貝 void mutableStrCopy(){ NSMutableString *str=[[NSMutableString alloc]initWithFormat:@"abcd"]; //會產生一個新的對象計數器1 NSString *str2=[str copy]; //輸出二者的地址,二者的地址是不同的 NSLog(@"str --> %p",str); NSLog(@"str2 --> %p",str2); }
同理,對於自定義對象的mutableCopy:必須實現 NSMutableCopying 協議,重寫 mutableCopyWithZone 方法。
在NSCopying協議中,其實只有一個協議方法:
@protocol NSCopying - (id)copyWithZone:(NSZone *)zone; @end
在NSMutableCopying協議:
@protocol NSMutableCopying - (id)mutableCopyWithZone:(NSZone *)zone; @end
#import@interface Person : NSObject @property(nonatomic,assign)int age; @property(nonatomic,copy)NSString *name; -(instancetype)initWithAge:(int)age withName:(NSString*)name; @end
#import "Person.h" @implementation Person -(instancetype)initWithAge:(int)age withName:(NSString*)name{ self = [super init]; if (self) { self.age = age; self.name = name; } return self; } -(id)copyWithZone:(NSZone *)zone{ Person* person = [[[self class] allocWithZone:zone] initWithAge:self.age withName:self.name]; return person; } @end
int main(int argc, const char * argv[]) { @autoreleasepool { Person *p1 = [[Person alloc] initWithAge:20 withName:@"Jack"]; Person *p2 = [p1 copy]; //輸出兩個 Person 對象的地址,二者是不同的 NSLog(@"p1 --> %p",p1); NSLog(@"p2 --> %p",p2); } return 0; }
-(id)copyWithZone:(NSZone *)zone{ return self; }這樣,輸出的兩個對象的地址就是相同的了。
下面了解一下關於如果某一個自定義類繼承了 這個Person類的情況。
如果某一個子類繼承了實現了NSCopying協議的基類,那麼該子類也是會自動繼承這個協議的方法。但是需要自己重新實現。
例如:有一個Student子類繼承了這個Person類:
#import#import "Person.h" @interface Student : Person @property(nonatomic,copy)NSString *school; -(instancetype)initWithAge:(int)age withName:(NSString *)name WithSchool:(NSString*)school; @end
#import "Student.h" @implementation Student -(instancetype)initWithAge:(int)age withName:(NSString *)name WithSchool:(NSString*)school{ self = [super initWithAge:age withName:name]; if (self) { self.school = school; } return self; } -(id)copyWithZone:(NSZone *)zone{ Student *student = [super copyWithZone:zone]; student.school = self.school; return student; } @end