iOS底层原理之`OC语法`(深拷贝和浅拷贝)

42 篇文章 0 订阅

1.深拷贝和浅拷贝概念

深拷贝和浅拷贝

  • 由上面的图我们可以明确地看出:

    1. 浅拷贝(Shallow copy):是指针复制,它们指向共同的内存地址,没有开辟新的空间,当内存销毁的时候,指向这片内存的几个指针需要重新定义才可以使用,要不然会成为野指针。相当于对象做一次retain操作,引用计数加1。

    2. 深拷贝(Deep copy):是指内容拷贝,拷贝后的对象会分配新的内存空间,两个对象虽然存的值是相同的,但是内存地址不一样,两个对象也互不影响,互不干涉。源对象的引用计数不变,副本对象的引用计数为1。

    3.区别总结:浅拷贝就是指针拷贝,深拷贝就是内容拷贝;质区别在于是否开启新的内存空间、是否影响内存地址的引用计数。

2.copy和mutableCopy

  • copy拷贝出来的对象类型总是不可变类型,
  • mutableCopy拷贝出来的对象类型总是可变类型(下面示1例可以证明)。

3.OC中的非集合类对象和集合类对象的深浅拷贝

一. 非集合类对象:NSString,NSMutableString,NSData,NSNumber
非集合对象的copymutableCopy
示例1:

        NSString *str1 = @"字符串1";
        NSString *str2 = [str1 copy];
        NSMutableString *str3 = [str1 mutableCopy];
        
        NSMutableString * str4 = [NSMutableString stringWithString:@"字符串2"];
        NSString * str5 = [str4 copy];
        NSMutableString *str6 = [str4 mutableCopy];

打断点lldb调试:
非集合对象的copy与mutableCopy.png

  • 在此解释一下在runtimeNSString的“真身”是__NSCFConstantStringNSMutableString的“真身”是__NSCFString,然后我们就能很清楚的看到,只要是copy得到的值就是不可变类型,而mutablecopy得到的是可变类型。但是str5 的类型显示是__NSCFString类型,应该是__NSCFConstantString类型才对啊,这里是为什么呢?个人猜测是子类指针指向父类(继承多太的一种体现),其本质还是父类,调NSMutableString的特有方法如运行 [str5 appendFormat:@"sss"]的时候会crash,原因就是str5本质是NSString类型不能调用子类NSMutableString特有的方法。
  • 结果分析:
    1.对不可变非集合对象进行 copy ,属于浅拷贝,不产生新的对象。
    2.对不可变非集合对象 进行 mutableCopy ,属于深拷贝,产生新的可变对象。
    3.对 可变非集合对象 进行 copy, 属于深拷贝,产生新的不可变对象。
    4.对可变非集合对象进行mutableCopy,属于深拷贝, 产生新的可变对象。
    说明:上面所说的对象其实不大合适,对象本质就是指针,上面所说的对象其实是指分配的内存,便于理解。
    二.集合类对象:NSArray,NSMutableArray,NSDictionary,NSSet...
    集合对象的copymutableCopy
    示例2:
        NSArray * arr1 = [NSArray arrayWithObjects:@1,@2,@3,nil];
        NSArray * arr2 = [arr1 copy];
        NSMutableArray * arr3 = [arr1 mutableCopy];
        
        NSMutableArray * arr4 = [NSMutableArray arrayWithObjects:@4,@5,@6,nil];
        NSArray * arr5 = arr4.copy;
        NSMutableArray * arr6 = arr4.mutableCopy;

打断点lldb调试:
集合对象的copy与mutableCopy.png

  • 结果分析:
    1.对不可变集合对象进行 copy ,属于浅拷贝,不产生新的对象。
    2.对不可变集合对象 进行 mutableCopy ,属于单层深拷贝,产生新的可变对象。
    3.对 可变集合对象 进行 copy, 属于单层深拷贝,产生新的不可变对象。
    4.对可变集合对象进行mutableCopy,属于单层深拷贝, 产生新的可变对象。
    单层深拷贝:是指集合对象的内容复制仅限于对象本身,对象元素仍然是指针复制,列如上面的arr3[arr1 mutableCopy]产生的,产生的arr3是可变的数组,arr3里面的NSNumber元素只是指针复制并没有进行深复制。
  • 不管是集合对象还是非集合对象都遵从以下规律:
源对象类型拷贝方式目标对象类型是否开辟新内存拷贝类型
不可变对象copy不可变对象浅拷贝(指针拷贝)
不可变对象mutableCopy可变对象深拷贝(内容拷贝)
可变对象copy不可变对象深拷贝(内容拷贝)
可变对象mutableCopy可变对象深拷贝(内容拷贝)

4.property中copy关键字

  • 当我们使用一个copy关键字声明一个对象的时候, 调用 setter 方法的时候,copy关键字会为对象自动copy一个副本。
    示例3:
@property (nonatomic, copy) NSArray *array;

- (void)setArray:(NSArray *)array {
_array = [array copy];  
}

此时不管传进来的arrayNSArray还是NSMutableArray类型都会产生一个array copy 了一个副本,是不可变的。

如果我们直接用strong关键字的话,又是怎样的呢?
示例4:

@property (nonatomic, strong) NSArray *array;

- (void)setArray:(NSArray *)array {
_array = array;  
}

如果此时传入的array是一个NSMutableArray的话,self.array可能会在不知情的情况下被修改。

  • 为什么用@property声明的NSString(或NSArrayNSDictionary)等不可变属性经常使用copy关键字?使用strong关键字,会有什么问题?
    示例5:
@interface Person : NSObject
@property(nonatomic ,strong) NSString * name;
@property(nonatomic ,copy) NSString * age;
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person * p = [[Person alloc]init];
        NSMutableString * name = [NSMutableString stringWithFormat:@"马云爸爸"];
        p.name = name;
        NSLog(@"name修改前:%@", p.name);
        [name appendString:@"和马化腾岳父谁厉害?"];
        NSLog(@"name修改后:%@", p.name);
        
        NSMutableString * age = [NSMutableString stringWithFormat:@"马云爸爸50岁"];
        p.age = age;
        NSLog(@"age修改前:%@", p.age);
        [age appendString:@"马化腾岳父45岁"];
        NSLog(@"age修改后:%@", p.age);
    }
    return 0;
}

打印结果
property中copy关键字.png
结果分析:Personname属性用了strong修饰,当传进来的NSMutableString类型的name变量值改变后Personname属性值也发生了改变(父类指针指向子类,多态),而我们并没有对Personname属性直接操作,这样是不合理的;而Personage属性用了copy修饰,不管传进来的变量是不是可变的字符串,都只会拷贝一份指针而指向该块内存,当可变的字符串age值发生改变时(内存发生了改变),属性age还是指向该块内存,不会改变。

  • 如果用copy修饰可变对象会产生什么后果?
    假如我们用copy关键字 来声明一个NSMutableString对象。
    示例6:
@interface Person : NSObject
@property(nonatomic ,copy) NSMutableString * name;
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person * p = [[Person alloc]init];
        NSMutableString * name = [NSMutableString stringWithFormat:@"马云爸爸"];
        p.name = name;
        [p.name appendString:@"和马化腾岳父谁厉害?"];
    }
    return 0;
}

copy修饰可变对象.png
crash原因是:尝试使用appendString改变不可变对象

5.自定义对象使用copy和mutableCopy。

如果想自定义对象使用copymutableCopy,只需要分别遵守NSCopying,NSMutableCopying协议,然后实现- (id)copyWithZone:(nullable NSZone *)zone;- (id)mutableCopyWithZone:(nullable NSZone *)zone;方法。
示例7:

#import <Foundation/Foundation.h>
@interface Person : NSObject<NSCopying,NSMutableCopying>
@property(nonatomic ,copy) NSString * name;
@property(nonatomic ,copy) NSString * age;
@end

#import "Person.h"
@implementation Person
-(id)copyWithZone:(NSZone *)zone{
    Person * p = self;
    return p;
}
-(id)mutableCopyWithZone:(NSZone *)zone{
    Person * p = [[Person allocWithZone:zone] init];
    p.name = self.name;
    p.age = self.age;
    return p;
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person * p1 = [[Person alloc]init];
        p1.name = @"马云";
        p1.age = @"55";
        Person * p2 = [p1 copy];
        Person * p3 = [p1 mutableCopy];
    }
    return 0;
}

自定义对象使用copy和mutableCopy.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值