美文网首页
iOS开发之深拷贝与钱拷贝

iOS开发之深拷贝与钱拷贝

作者: 黄成瑞 | 来源:发表于2020-05-25 07:29 被阅读0次

    深拷贝和浅拷贝 以及各个修饰词的区别

    1.Copy和Strong
    (1)修饰不可变的字符串
    示例代码:
    @property (strong, nonatomic) NSString *strStrong;
    @property (copy, nonatomic) NSString *strCopy;

    NSString *str = @"aaaa";
    NSLog(@"str = %@ 内存地址 = %p 指针地址 = %p",str,str,&str);
    self.strStrong = str;
    NSLog(@"strong = %@ 内存地址 = %p 指针地址 = %p",self.strStrong,self.strStrong,&_strStrong);
    self.strCopy = str;
    NSLog(@"copy = %@ 内存地址 = %p 指针地址 = %p\n",self.strCopy,self.strCopy,&_strCopy);


    str = @"bbb";
    NSLog(@"str = %@ 内存地址 = %p 指针地址 = %p",str,str,&str);
    NSLog(@"strong = %@ 内存地址 = %p 指针地址 = %p",self.strStrong,self.strStrong,&_strStrong);
    NSLog(@"copy = %@ 内存地址 = %p 指针地址 = %p\n",self.strCopy,self.strCopy,&_strCopy);

    打印结果:
    2019-05-05 11:11:08.840288+0800 TTT[3684:27977] str = aaaa 内存地址 = 0x108dfe098 指针地址 = 0x7ffee6e00a98
    2019-05-05 11:11:08.840362+0800 TTT[3684:27977] strong = aaaa 内存地址 = 0x108dfe098 指针地址 = 0x7fe957417710
    2019-05-05 11:11:08.840426+0800 TTT[3684:27977] copy = aaaa 内存地址 = 0x108dfe098 指针地址 = 0x7fe957417718


    2019-05-05 11:11:08.840575+0800 TTT[3684:27977] str = bbb 内存地址 = 0x108dfe118 指针地址 = 0x7ffee6e00a98
    2019-05-05 11:11:08.840658+0800 TTT[3684:27977] strong = aaaa 内存地址 = 0x108dfe098 指针地址 = 0x7fe957417710
    2019-05-05 11:11:08.840731+0800 TTT[3684:27977] copy = aaaa 内存地址 = 0x108dfe098 指针地址 = 0x7fe957417718

    结论:源对象为不可变字符串而言,不论使用copy还是strong,所对应的值是不会发生变化的,strong和copy不会开辟新的内存,固不是深拷贝,属于浅拷贝。
    补充:此处使用的是self.赋值,如果使用下划线结果是一样的,此处的self.是浅拷贝

    (2)修饰可变的字符串
    示例代码:
    @property (strong, nonatomic) NSString *strStrong;
    @property (copy, nonatomic) NSString *strCopy;

    NSMutableString *mutableStr = [[NSMutableString alloc] initWithString:@"aaa"];
    NSLog(@"mutableStr = %@ 内存地址 = %p 指针地址 = %p",mutableStr,mutableStr,&mutableStr);
    self.strStrong = mutableStr;
    NSLog(@"strong = %@ 内存地址 = %p 指针地址 = %p",self.strStrong,self.strStrong,&_strStrong);
    self.strCopy = mutableStr;
    NSLog(@"copy = %@ 内存地址 = %p 指针地址 = %p\n",self.strCopy,self.strCopy,&_strCopy);

    [mutableStr appendString:@"bbb"];
    NSLog(@"mutableStr = %@ 内存地址 = %p 指针地址 = %p",mutableStr,mutableStr,&mutableStr);
    NSLog(@"strong = %@ 内存地址 = %p 指针地址 = %p",self.strStrong,self.strStrong,&_strStrong);
    NSLog(@"copy = %@ 内存地址 = %p 指针地址 = %p",self.strCopy,self.strCopy,&_strCopy);

    打印结果:
    2019-05-05 11:23:18.961313+0800 TTT[3728:32476] mutableStr = aaa 内存地址 = 0x600002944030 指针地址 = 0x7ffee8f8ca98
    2019-05-05 11:23:18.961510+0800 TTT[3728:32476] strong = aaa 内存地址 = 0x600002944030 指针地址 = 0x7fb61f612810
    2019-05-05 11:23:18.961594+0800 TTT[3728:32476] copy = aaa 内存地址 = 0x8d76ba2dd400d6bd 指针地址 = 0x7fb61f612818

    2019-05-05 11:23:18.961682+0800 TTT[3728:32476] mutableStr = aaabbb 内存地址 = 0x600002944030 指针地址 = 0x7ffee8f8ca98
    2019-05-05 11:23:18.961835+0800 TTT[3728:32476] strong = aaabbb 内存地址 = 0x600002944030 指针地址 = 0x7fb61f612810
    2019-05-05 11:23:18.961946+0800 TTT[3728:32476] copy = aaa 内存地址 = 0x8d76ba2dd400d6bd 指针地址 = 0x7fb61f612818

    结论:数据源为可变字符串而言,使用copy申明属性,会开辟一块新的内存空间存放值,源数据无论怎样变化,都不会影响copy属性中的值,属于深拷贝。使用strong申明属性,不会开辟新的内存空间,只会引用到数据源内存地址,因此数据改变,则strong属性也会改变,属于浅拷贝。
    补充:此处使用的是self.赋值,如果使用下划线,则还是“aaa”,因为@property声明属性变量时,会自动生成下划线+属性名命名的实例变量,并生成其对应的getter、setter方法(@synthesize copyyStr = _copyyStr),self.赋值时,会调用setter方法,而下划线是直接赋值,并不会调用setter方法

    (3)修饰可变数组、可变字典
    示例代码:
    @property (copy, nonatomic) NSMutableArray *originArray_0;
    @property (strong, nonatomic) NSMutableArray *originArray_1;

    self.originArray_0 = [NSMutableArray arrayWithCapacity:0];
    self.originArray_1 = [NSMutableArray arrayWithCapacity:0];
    NSLog(@"self. originArray_0 class name %@",[self.originArray_0 class]);
    NSLog(@"self. originArray_1 class name %@",[self.originArray_1 class]);

    打印结果:
    2019-05-05 11:33:17.006850+0800 TTT[3920:36550] self. originArray_0 class name __NSArray0
    2019-05-05 11:33:17.006963+0800 TTT[3920:36550] self. originArray_1 class name __NSArrayM

    结论:copy申明可变数据,初始化或赋值后,变成不可变数组,对数组执行增删会出错误,这是因为copy属性修饰后,在初始化或赋值时,会先执行copy操作,然后赋值。所以开发中对可变数组、字典使用strong属性修饰
    扩展:
    (1)NSArray0:不可变的数组
    (2)NSArrayM:NSMutbleArray *arr4 = [NSMutbleArray array];---初始化后的可变数组类名
    (3)NSArrayI:NSArray *arr2 = [[NSArray alloc]init];---初始化后的不可变数组类名
    (4)NSPlaceHolderArray:NSArray *arr3 = [NSArray alloc];---alloc但未初始化init的

    汇总:
    浅拷贝:指针的复制,指向同一块内存。
    深拷贝:内存的复制,指向不同的内存,互不干涉。
    1.当原字符串是NSString时,因为其是不可变字符串,所以无论使用strong还是copy修饰,都指向原来的对象,copy操作只是做了一次浅拷贝。
    2.当原字符串是NSMutableString时,strong只是将原字符串的引用计数+1,而copy则是对原字符串做了次深拷贝,从而生成了一个新对象,并且copy的对象指向了这个新对象。
    所以如果字符串是NSMutableString,使用strong只会增加引用计数,但是copy会执行一次深拷贝,会造成不必要的内存浪费。而如果原字符串是NSString,strong和copy效果一样不会存在这个问题,但是我们一般声明NSString时,不希望它改变,所以一般情况下建议使用copy,这样可以避免NSMutableString带来的错误。
    3.所以,在我们写代码过程中,一般不希望NSString改变,固然声明为copy,我们对NSMutalbleArray时,我们需要它改变,所以声明为Strong,Copy会让可变变为不可变的哦(很重要、牢牢记住)

    2.Copy和retain
    a.copy其实是建立了一个相同的对象,而retain不是
    b.copy是内容拷贝,retain是指针拷贝
    c.copy是内容拷贝,对于像NSString,的确是这样,但是如果copy是一个NSArray呢?这时只是copy指向了array中相对应元素的指针,这便是浅拷贝。

    3.assign和retain
    a.assign:简单赋值,不更改索引计数
    b.assign就是直接赋值
    c.retain使用了引用计数,retain引用计数+1,release引用计数-1,当引用计数为0时,dealloc函数被调用,内存被回收。

    4.weak和strong
    a.默认是strong,只有一种情况你需要使用weak,那就是防止循环引用,A类包含B类,B类包含了A类,这时候要使用weak。
    b.声明weak的指针,指针指向的地址一旦被释放,这些指针都将被置为nil,可以有效的防止野指针。
    c.strong和weak是ARC引入的对象变量属性,ARC下的strong相当于MRC下的retain,ARC下的weak相当于MRC下的assign

    5.retain和strong
    在ARC机制下,retain和strong基本相同,MRC下可以看到不同,MR下strong修饰Block内部进行了Copy(retain是NSStackBlock,strong和copy是MSMallocBlock)
    6.weak和assign
    weak比assign多了一个功能,当对象小时候自动把指针变成nil,而strong等同于retain

    相关文章

      网友评论

          本文标题:iOS开发之深拷贝与钱拷贝

          本文链接:https://www.haomeiwen.com/subject/lukjnhtx.html