随笔---iOS copy && strong

最近面试提到过copy和strong 这里自己写demo来巩固基础

  1. 拷贝:即复制,目的是产生副本,让原对象和副本相互独立,互不影响;
  2. 不可变拷贝:即 copy 方法,无论原对象是否可变,都产生不可变副本;
  3. 可变拷贝:即 mutableCopy 方法,无论原对象是否可变,都产生可变副本;
  4. 深拷贝:内容拷贝,产生新的对象;
  5. 浅拷贝:指针拷贝,不产生新的对象;

由上可知,copy 和深拷贝是两个概念,两者并不一定相等,先给结果:

  • 源对象不可变时,copy 方法就是浅拷贝;
  • 源对象可变时,copy 方法就是深拷贝;
  • mutableCopy 方法无论何种情况都是深拷贝;

@interface ViewController ()

@property (nonatomic,strong) NSString *strongStr;

@property (nonatomiccopy)   NSString *copyyStr;

@property (nonatomiccopy)   NSMutableString *mutaCopyyStr;

@end

- (void)test1{

    NSString *originStr1 = [NSString stringWithFormat:@"hello, everyone"];

    _strongStr = originStr1;

    _copyyStr = originStr1;

    NSLog(@"第一种场景:用NSString直接赋值");

    NSLog(@"               对象地址         对象指针地址        对象的值   ");

    NSLog(@"originStr: %p , %p , %@", originStr1, &originStr1, originStr1);

    NSLog(@"strongStr: %p , %p , %@", _strongStr, &_strongStr, _strongStr);

    NSLog(@" copyyStr: %p , %p , %@", _copyyStr, &_copyyStr, _copyyStr);

}

strong和copy修饰的对象指向源内存地址,strong只是增加了对象的引用计数,copy只是拷贝了对象指针地址

- (void)test2{

    NSMutableString *originStr1 = [NSMutableString stringWithFormat:@"hello, everyone"];

    _strongStr = originStr1;

    _copyyStr = originStr1;

    NSLog(@"第2种场景:用NSMutableString直接赋值");

    [originStr1 setString:@"hello, world"];

    NSLog(@"               对象地址         对象指针地址        对象的值   ");

    NSLog(@"originStr: %p , %p , %@", originStr1, &originStr1, originStr1);

    NSLog(@"strongStr: %p , %p , %@", _strongStr, &_strongStr, _strongStr);

    NSLog(@" copyyStr: %p , %p , %@", _copyyStr, &_copyyStr, _copyyStr);

}

copyyStr 在声明的时候用copy修饰的浅拷贝,所以源内存会有增加三个引用计数。

 

- (void)test3{

    NSMutableString *originStr1 = [NSMutableString stringWithFormat:@"hello, everyone"];

    _strongStr = originStr1;

    _copyyStr = [originStr1 copy];

    NSLog(@"第3种场景:用NSMutableString copy直接赋值");

    [originStr1 setString:@"hello, world"];

    NSLog(@"               对象地址         对象指针地址        对象的值   ");

    NSLog(@"originStr: %p , %p , %@", originStr1, &originStr1, originStr1);

    NSLog(@"strongStr: %p , %p , %@", _strongStr, &_strongStr, _strongStr);

    NSLog(@" copyyStr: %p , %p , %@", _copyyStr, &_copyyStr, _copyyStr);

}

 

copyyStr所指向的对象地址和前面两个不一样,而且对象指针地址以及对象的值还是originStr1初始化的内容;NSMutableString *originStr1,

_copyyStr = [originStr1 copy]; 执行[originStr1 copy]代码之后 会在内存中开辟新的内存(深拷贝)存放originStr1初始化的内容,返回一个地址。

 

- (void)test4{

    NSMutableString *originStr1 = [NSMutableString stringWithFormat:@"hello, everyone"];

    self.strongStr = originStr1;

    self.copyyStr = originStr1;

    NSLog(@"第4种场景:用self. 直接赋值");

    [originStr1 setString:@"hello, world"];

    NSLog(@"               对象地址         对象指针地址        对象的值   ");

    NSLog(@"originStr: %p , %p , %@", originStr1, &originStr1, originStr1);

    NSLog(@"strongStr: %p , %p , %@", _strongStr, &_strongStr, _strongStr);

    NSLog(@" copyyStr: %p , %p , %@", _copyyStr, &_copyyStr, _copyyStr);

}

self.copyyStr 会转化为setter方法,该方法内部会实现 [copyy copy]的操作 进行了深拷贝,生成一个新的对象赋值给_copyyStr。

 

- (void)test5{

    NSString *originStr1 = [NSString stringWithFormat:@"hello, everyone"];

    _mutaCopyyStr = [originStr1 copy];

    NSLog(@"               对象地址         对象指针地址        对象的值   ");

    NSLog(@"originStr: %p , %p , %@", originStr1, &originStr1, originStr1);

    NSLog(@"originStr: %p , %p , %@", _mutaCopyyStr, &_mutaCopyyStr, _mutaCopyyStr);

}

_mutaCopyyStr 浅拷贝 ,源内存增加了引用计数。

 

- (void)test7{

    NSMutableString *originStr1 = [NSMutableString stringWithFormat:@"hello, everyone"];

    _mutaCopyyStr = [originStr1 copy];

    [originStr1 setString:@"hello world"];

    NSLog(@"               对象地址         对象指针地址        对象的值   ");

    NSLog(@"originStr: %p , %p , %@", originStr1, &originStr1, originStr1);

    NSLog(@"mutaCopyyStr: %p , %p , %@", _mutaCopyyStr, &_mutaCopyyStr, _mutaCopyyStr);

}

_mutaCopyyStr = [originStr1 copy];进行一个深拷贝,开辟新的内存空间,_mutaCopyyStr还是指向内存内容还是hello, everyone

 

 

- (void)test8{

    NSString *originStr1 = [NSString stringWithFormat:@"hello, everyone"];

    _mutaCopyyStr = originStr1;

    [_mutaCopyyStr setString:@"4444444"];

    NSLog(@"               对象地址         对象指针地址        对象的值   ");

    NSLog(@"originStr: %p , %p , %@", originStr1, &originStr1, originStr1);

    NSLog(@"mutaCopyyStr: %p , %p , %@", _mutaCopyyStr, &_mutaCopyyStr, _mutaCopyyStr);

}

思路:打印 _mutaCopyyStr所对应的类型  NSLog(@"_mutaCopyyStr kind===%@", NSStringFromClass([_mutaCopyyStr class]));

提示已经很明显了,尝试利用setString 来更改不可更改对象。

那么执行 [_mutaCopyyStr setString:@"4444444"]; 就会报错

如何修改呢?_mutaCopyyStr = [originStr1 mutableCopy]; 深拷贝改成可变对象;

Copy总结:

源对象类型

拷贝方式

目标对象类型

拷贝类型

NSMutableNSString

copy

NSSString

深拷贝

NSMutableNSString

mutableCopy

NSMutableNSString

深拷贝

NSSString

copy

NSSString

浅拷贝

NSSString

mutableCopy

NSMutableNSString

深拷贝

copy 修饰源对象后变成不可变对象 

最后回答一道面试题

@property (nonatomiccopy)   NSMutableString *mutaCopyyStr;

NSMutableString 对象 copy 修饰源对象后变成NSString类型,

那么如果赋值为一个NSMutableString 经过copy操作变成NSString 那么使用 [_mutaCopyyStr setString:@"4444444"];的时候就会报错

那么如果赋值为一个NSString 经过copy操作变成NSString 。所以为了保险起见还是用copy来修饰NSString属性 一个字安全

 

  • copy 的目的是创建一个互不干扰,相互独立的副本;
  • copy 无论是直接调用还是修饰属性,其本质是调用copyWithZonemutableCopyWithZone方法;
  • 深浅复制的区别在于返回值是否为新创建的对象,和调用 copy 的哪个方法无关;
  • 使用 copy 修饰属性的关键目的是告诉使用者,这个不要直接修改属性所指向内存中的值;
  • 修饰可变类型的对象,比如可变数组,严禁使用 copy 修饰;
  • copy 的本质是调用 copy 协议中的两个方法,只是系统对字符串、数组、字典、NSNumber 和 Block 实现了该协议的两个方法,其中两个方法所实现的逻辑大同小异;
  • copy 修饰属性的本质是自动调用新值的 copy 方法以获取一个不可变对象,属性无 mutableCopy 修饰,因为没有必要;
  • copy 修饰 Block 属性的本质仍然是调用 copy 方法,只是其内部实现是将存放在栈上的 block 转移到堆上,否则栈中的 Block 被销毁后会访问指向该 Block 的指针会产生坏内存访问问题;


作者:康小曹
链接:https://juejin.cn/post/6844904033019232264
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值