Deep/Shallow Copy

原文連接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.

相關文章
相關標籤/搜索