iOS 浅拷贝和深拷贝的区别? copy和mutableCopy的区别?

时间:2021-09-05 19:50:39

1.概念

     我们在声明一个变量时怎么给变量赋值呢?

     第一种情况:将其它变量的值直接拿过来赋值;

                            例如Person *person = 0x100104520;      Person *newPerson = person;

     第二种情况:自己制造想要的值; Person *newPerson = [ [Person alloc]  init];

      浅拷贝和深拷贝都是说引用数据类型,不是说基本数据类型

      浅拷贝是说在给一个变量赋值的是将另一个变量的引用(内存地址)赋值给新变量,而不是又重新造了一个新的东西来

      深拷贝是将重新制造出一个变量的副本,然后将变量的副本赋值给新变量


      浅拷贝:指针拷贝,不产生新的对象,源对象的引用计数器+1;

      深拷贝:对象拷贝,会产生新的对象,源对象的引用计数器不变;


     判断是浅拷贝和深拷贝就看一下两个变量的内存地址是否一样,一样就是浅拷贝,不一样就是深拷贝,也可以改变一个变量的其中一个属性值看两者的值都会发生变化;

    

     copy:拷贝的结果是一个不可变(imutable)的对象, 无论源对象是可变的还是不可变的,copy之后的都是不可变的类型 

                 不可变类型   变量名 =  [不可变类型|可变类型  copy];

     mutableCopy:可变拷贝的结果的数据类型是一个可变的对象,无论源对象时不可变的还是可变的,可变拷贝之后的数据类型都是可变类型

                                 可变类型  变量名 = [不可变类型|可变类型  mutableCopy];

    copy对引用计数器的影响:

             拷贝一个不可变的类型的结果是新对象和源对象都指向同一个内存地址,即使指针拷贝,属于浅拷贝,所以不生产新对象,源对象的引用计数+1

             拷贝一个可变的类型,会生成一个新对象,不影响源对象的引用计数

    mutableCopy对引用计数器的影响:

             无论对可变类型或者对不可变类型使用mutableCopy操作,都不会影响源对象的引用计数

    

    除了对一个不可变的类型进行拷贝操作外会对源对象的引用计数+1,其他情况(无论是对可变类型或不可变类型   进行拷贝或者可变拷贝)都会生成一个全新的对象(对象的引用计数器从0到1),都不会影响源对象的引用计数。


    copy一般不会对源对象的引用计数+1,即使对自定义的对象(实现了NSCopying协议)进行copy也不会影响源对象的引用计数,除了 [不可变类型 copy]例外,因为这种情况特殊,因为:对一个不可变类型的对象copy之后也是不可变的类型,既然不可变也没法修改,再生成一份新的对象感觉在内存上有些浪费,还不如对源对象的引用计数+1,这样既达到节约内存的目的,也不破坏内存管理规则。copy会是新对象的引用计数器值为1

   

   重要的事情所三遍,再一次强调一下一种特殊情况,对 不可变类型 copy操作会,是一种指针拷贝,是浅拷贝,源对象和新对象会指向同一块内存地址,copy之后会是引用计数器+1


测试代码:

#import <Foundation/Foundation.h>
#import "User.h"


// copy 不可变的对象,OC对该类型的操作进行了特殊优化
// copy 会得到一个全新的对象,新的内存地址,但是对于[NSArray copy]操作,并没有这样,这是因为系统对此进行了优化,减少内存的开销,但这样并不影响内存管理规则的使用,不用开辟新内存,只要要增加一个引用计数即可,注意系统只是对不可变类型 的 不可变复制 进行了优化,是的copy能使得引用计数+1,但并不使用与其他情况,包括自定义对象这种情况,也不能得出copy能使引用计数+1,内存法则中是说如果对一个对象copy,那么新的对象的计数将为1,是说的新对象,而不是源对象, 所以说copy并不能使源对象应用计数+1(不包括[NSArray copy]这种情况), 只能将新对象的引用计数置为1
void copyNotMutableType() {
    // 不可变数组
    NSArray *array = [NSArray arrayWithObjects:@"1", @"2", @"3", nil];
    NSLog(@"【array】 retainCount:%ld", [array retainCount]);  // 1
    
    NSArray *array2 = [array copy];
    NSLog(@"【array】 retainCount:%ld", [array retainCount]);  // 2
    
    // 难道copy也把对象的引用数量也拷贝了, retainCount也是该对象的一个实例变量,拷贝整个对象当然也会复制了该变量,所以和array 的retainCount的数一样
    NSLog(@"《array2》 retainCount:%ld", [array2 retainCount]); // 2
    
    [array2 retain];
    NSLog(@"《array2》 retainCount:%ld", [array2 retainCount]); // 3
    NSLog(@"【array】 retainCount:%ld", [array retainCount]);   // 3
    
    if (array == array2) {
        NSLog(@"同一个对象");
    }
    
    NSLog(@"【array】:%@", array);      // [1, 2, 3]
    NSLog(@"《array2》:%@", array2);    // [1, 2, 3]
    
    [array2 release];
    [array2 release];
    [array release];
}


// 对可变类型copy,不影响可变变量的引用计数
void copyMutableType() {
    NSMutableArray *mutableArray = [NSMutableArray arrayWithObjects:@"1", @"2", @"3", nil];
    NSLog(@"【mutableArray】 retainCount:%ld", [mutableArray retainCount]);   // 1
    [mutableArray retain];
    NSLog(@"【mutableArray】 retainCount:%ld", [mutableArray retainCount]);   // 2
    
    NSArray *array = [mutableArray copy];
    NSLog(@"【mutableArray】 retainCount:%ld", [mutableArray retainCount]);  // 2:对可变类型copy不会影响引用计数器
    NSLog(@"【array】 retainCount:%ld", [array retainCount]);  // 1
    
    [mutableArray addObject:@"4"];
    NSLog(@"mutableArray:%@", mutableArray);    // [1, 2, 3, 4]
    NSLog(@"array:%@", array);                  // [1, 2, 3]
}


// mutableCopy 可变拷贝一个非可变类型的对象 不会影响源对象的引用计数
void mutableCopyNotMutableType() {
    NSArray *array = [NSArray arrayWithObjects:@"1", @"2", @"3", nil];
    NSLog(@"【array】 retainCount:%ld", [array retainCount]);  // 1
    [array retain];
    NSLog(@"【array】 retainCount:%ld", [array retainCount]);  // 2
    NSMutableArray *mutableArray = [array mutableCopy];
    NSLog(@"【array】 retainCount:%ld", [array retainCount]);  // 2
    NSLog(@"《mutableArray》 retainCount:%ld", [mutableArray retainCount]);  // 1
    
    [mutableArray addObject:@"4"];
    NSLog(@"mutableArray:%@", mutableArray);    // [1, 2, 3, 4]
    NSLog(@"array:%@", array);                  // [1, 2, 3]
}

//
void mutableCopyMutableType(){
    NSMutableArray *mutableArray = [NSMutableArray arrayWithObjects:@"1", @"2", @"3", nil];
    NSLog(@"【mutableArray】 retainCount:%ld", [mutableArray retainCount]);   // 1
    [mutableArray retain];
    NSLog(@"【mutableArray】 retainCount:%ld", [mutableArray retainCount]);   // 2
    
    NSMutableArray *mutableCopyArray = [mutableArray mutableCopy];
    NSLog(@"【mutableArray】 retainCount:%ld", [mutableArray retainCount]);   // 2
    NSLog(@"《mutableCopyArray》retainCount:%ld", [mutableCopyArray retainCount]);  // 1
    
    [mutableArray addObject:@"4"];
    NSLog(@"mutableArray:%@", mutableArray);            // [1, 2, 3, 4]
    NSLog(@"mutableCopyArray:%@", mutableCopyArray);    // [1, 2, 3]
}


void test(){
    NSArray *array = [NSArray arrayWithObjects:@"1", @"2", @"3", nil];
    NSLog(@"【array】 retainCount:%ld", [array retainCount]);
    [array copy];
    NSLog(@"【array】 retainCount:%ld", [array retainCount]);
}

void testObject(){
    User *user = [[User alloc] init];
    user.username = @"zhangsan";
    user.age = 25;
    NSLog(@"【user】 retainCount:%ld", [user retainCount]);
    
    // copy 自定义对象难到引用计数不+1?
    User *copyUser = [user copy];
    NSLog(@"【[user]】 retainCount:%ld", [user retainCount]);
    
    NSLog(@"【copyUser】 retainCount:%ld", [copyUser retainCount]);
    
    copyUser.username = @"lisi";
    NSLog(@"copyUser:%@", copyUser);
    NSLog(@"user:%@", user);
    
    [copyUser release];// 遵守内存管理规则
    [user release];
}
int main(int argc, const char * argv[]) {
//    Person *xiaohong = [[Person alloc] init];
//    NSString *name = @"xiaohong";
//    [xiaohong setName:name];
//    [xiaohong setName:name];
//    [xiaohong setName:name];
//    NSLog(@"4 retainCount : %ld", [[xiaohong name] retainCount]);
    
    copyNotMutableType();
    copyMutableType();
    mutableCopyNotMutableType();
    mutableCopyMutableType();
    test();
    testObject();
    return 0;
}

#import <Foundation/Foundation.h>
@interface User : NSObject <NSCopying>

@property (copy, nonatomic) NSString *username;
@property (assign, nonatomic) int *age;

@end


#import "User.h"
@implementation User

- (id)init {
    if (self = [super init]) {
        
    }
    
    return self;
}


- (id)copyWithZone:(nullable NSZone *)zone {
    User *copyUser = [[self class] allocWithZone:zone];
    copyUser.username = self.username;
    copyUser.age = self.age;
    
    return copyUser;
}
@end