原文連接html
拷貝是咱們在開發中常常使用的技巧,這裏指的不是到Github上去複製粘貼代碼,而是對內存中對象的操做 (逃數組
深拷貝與淺拷貝的區別 ? 深拷貝是指咱們拷貝出來的對象擁有本身單獨的內存地址,修改新對象不影響源對象,淺拷貝指的是在copy指針的引用,修改新對象會影響到源對象app
在ObjC裏面主要有兩個方法對對象進行拷貝ui
- (id)copy;
- (id)mutableCopy;
複製代碼
要對象可以使用這兩個方法須要遵照協議 NSCopying, NSMutableCopyingatom
那麼,該什麼時候使用這兩種方法呢, 先說結論,只有不可變對象調用copy方法的時候纔是淺拷貝,其餘狀況均爲深拷貝 新建一個工程驗證一下吧spa
Xcode -> New -> MacOS -> CommandLine -> main.m指針
因爲NSString 同時實現了 NSCopying, NSMutableCopying 兩個協議,咱們就用他來作實驗code
NSString *str1 = @"str1";
NSString *str2 = str1.copy;
NSString *str3 = str1.mutableCopy;
NSLog(@"%p %p %p",str1, str2, str3);
複製代碼
運行以後能夠看到以下輸出orm
0x1000020b8 0x1000020b8 0x100508e00
複製代碼
由此能夠得出結論,不可變對象使用 mutableCopy 爲深拷貝 ,copy 爲淺拷貝htm
下面驗證一下可變對象 NSMutableString
NSMutableString *str1 = [NSMutableString stringWithFormat:@"str1"];
NSString *str2 = str1.copy;
NSString *str3 = str1.mutableCopy;
NSLog(@"%p %p %p",str1, str2, str3);
0x100505260 0x50602036b4ec25f 0x100505290
複製代碼
得出結論,可變對象不管使用 copy 仍是 mutableCopy ,均爲深拷貝
這些是非集合類型的拷貝結論,那麼對於集合類型來講呢
新建Person類
Person.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
@property (nonatomic ,copy) NSString *name;
@property (nonatomic ,assign) NSUInteger age;
@property (nonatomic ,copy) NSArray<Person *> *friends;
@end
NS_ASSUME_NONNULL_END
複製代碼
Person.m
#import "Person.h"
@implementation Person
- (instancetype)init {
self = [super init];
if (self) {
_name = @"";
_age = 0;
_friends = @[];
}
return self;
}
// 重寫以便打印對象的屬性
- (NSString *)description {
return [NSString stringWithFormat:@"- name: %@, age: %ld, friends: %@",self.name, self.age, self.friends];
}
@end
複製代碼
若是正常按照上面的來想,集合類型應該也是同樣 只有不可變對象調用copy方法的時候纔是淺拷貝,其餘狀況均爲深拷貝
下面建立Array來驗證想法
Person *Alice = Person.new;
Alice.name = @"Alice";
Alice.age = 18;
NSArray *arr1 = @[Alice];
NSArray *arr2 = arr1.copy;
NSArray *arr3 = arr1.mutableCopy;
NSLog(@"%p %p %p",arr1, arr2, arr3);
0x1030064c0 0x1030064c0 0x103004a90
複製代碼
NSArray吻合咱們上面的結論,下面看下NSMutableArray
Person *Alice = Person.new;
Alice.name = @"Alice";
Alice.age = 18;
NSMutableArray *arr1 = [NSMutableArray arrayWithArray:@[Alice]];
NSArray *arr2 = arr1.copy;
NSArray *arr3 = arr1.mutableCopy;
NSLog(@"%p %p %p",arr1, arr2, arr3);
0x1030051f0 0x103006a00 0x1030054a0
複製代碼
也溫和上面的結論,可是稍等
NSMutableArray *arr1 = [NSMutableArray arrayWithArray:@[Alice]];
NSArray *arr2 = arr1.copy;
NSLog(@"%p %p",arr1.firstObject, arr2.firstObject);
複製代碼
咱們能夠打印arr1 和 arr2 的第一個元素的地址,發現他們仍是指向同一個地址!
查看Apple關於DeepCopy的文檔
咱們發現,原來要對一個集合類型實現真正的深拷貝須要用這種方法, 而上面這種官方名稱爲【單層深拷貝】
NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:
[NSKeyedArchiver archivedDataWithRootObject:oldArray]];
複製代碼
好,那麼咱們來改造代碼
Person *Alice = Person.new;
Alice.name = @"Alice";
Alice.age = 18;
NSError *err;
NSMutableArray *arr1 = [NSMutableArray arrayWithArray:@[Alice]];
NSArray *arr2 = [NSKeyedUnarchiver unarchivedObjectOfClass:[NSArray class] fromData:[NSKeyedArchiver archivedDataWithRootObject:arr1 requiringSecureCoding:false error:&err] error:&err];
if (err) {
NSLog(@"%@",err);
}
NSLog(@"%p %p",arr1.firstObject, arr2.firstObject);
複製代碼
奇怪的是輸出arr2爲nil,看一下控制檯輸出
-[Person encodeWithCoder:]: unrecognized selector sent to instance 0x100706ac0
複製代碼
原來是咱們的Person沒有遵照協議 NSCoding , 除此以外咱們還須要遵照 NSSecureCoding 協議
改造後的Person.m
#import "Person.h"
@interface Person () <NSCoding, NSSecureCoding>
@end
@implementation Person
+ (BOOL)supportsSecureCoding {
return true;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:_name forKey:@"name"];
[aCoder encodeInteger:_age forKey:@"age"];
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
if (self) {
self.name = [aDecoder decodeObjectForKey:@"name"];
self.age = [aDecoder decodeIntegerForKey:@"age"];
}
return self;
}
- (instancetype)init {
self = [super init];
if (self) {
_name = @"";
_age = 0;
_friends = @[];
}
return self;
}
複製代碼
如今咱們打開main.m試一下
Person *Alice = Person.new;
Alice.name = @"Alice";
Alice.age = 18;
NSError *err;
NSMutableArray *arr1 = [NSMutableArray arrayWithArray:@[Alice]];
NSArray *arr2 = [NSKeyedUnarchiver unarchivedObjectOfClasses:[NSSet setWithObjects:[NSArray class],[Person class], nil] fromData:[NSKeyedArchiver archivedDataWithRootObject:arr1 requiringSecureCoding:true error:&err] error:&err];
if (err) {
NSLog(@"%@",err);
}
NSLog(@"%p %p",arr1.firstObject, arr2.firstObject);
0x100709880 0x100705c60
複製代碼
咱們發現數組裏面的對象地址也全都不同了,這纔是集合類型的真正深拷貝
thanks.