细说@property(四)

copy和strong

copy和strong的区别是面试中出现频率最高的,我们一般都知道,不可变对象属性修饰符使用copy,可变对象属性修饰符使用strong。

可变对象和不可变对象

  • Objective-C中存在可变对象和不可变对象的概念。像NSArray、NSDictionary、NSString这些都是不可变对象,像NSMutableArray、NSMutableDictionary、NSMutableString这些是可变对象。可变对象和不可变对象的区别是,不可变对象的值一旦确定就不能再修改。
- (void)testNotChange
{
    NSString *str = @"123";
    NSLog(@"str = %p",str);
    str = @"234";
    NSLog(@"after str = %p",str);

    NSMutableString *mutStr = [NSMutableString stringWithString:@"abc"];
    NSLog(@"mutStr = %p",mutStr);
    [mutStr appendString:@"def"];
    NSLog(@"after mutStr = %p",mutStr);
}

NSString是不可变对象,虽然在程序中修改了str的值,但是此处的修改实际上是系统重新分配了空间,定义了字符串,然后str重新指向了一个新的地址,这也是为何修改之后地址不一致的原因。而NSMutableString是可变对象,程序中改变了mutStr的值,且修改前后mutStr的地址一致

2019-01-31 18:02:41.350812+0800 TestClock[884:17969] str = 0x106ec1290
2019-01-31 18:02:41.350919+0800 TestClock[884:17969] after str = 0x106ec12d0
2019-01-31 18:02:41.457179+0800 TestClock[1000:21900] mutStr = 0x600002100540
2019-01-31 18:02:41.457261+0800 TestClock[1000:21900] after mutStr = 0x600002100540

不可变对象用strong会怎样?
上面说了,可变对象使用strong,不可变对象使用copy。那么,如果不可变对象使用strong来修饰,会有什么问题呢?

@property (nonatomic, strong) NSString *strongStr;

- (void)testStrongStr
{
    NSString *tempStr = @"123";
    NSMutableString *mutString = [NSMutableString stringWithString:tempStr];
    self.strongStr = mutString;  // 子类初始化父类
    NSLog(@"self str = %p  mutStr = %p",self.strongStr,mutString);   // 两者指向的地址是一样的
    [mutString insertString:@"456" atIndex:0];
    NSLog(@"self str = %@  mutStr = %@",self.strongStr,mutString);  // 两者的值都会改变,不可变对象的值被改变
}

首先明确一点,既然类型是NSString,那么则代表我们不希望testStr被改变,否则直接使用可变对象NSMutableString就可以了。另外需要提醒的一点是,NSMutableString是NSString的子类,对继承了解的应该都知道,子类是可以用来初始化父类的。

注意:我们定义的不可变对象strongStr,在开发者无感知的情况下被篡改了。所谓无感知,是因为开发者没有显示的修改strongStr的值,而是再修改其他变量的值时,strongStr被意外的改变。这显然不是我们想得到的,而且也是危险的。项目中出现类似的bug时,通常都很难定位。这就是不可变对象使用strong修饰所带来的风险。

可变对象用copy会怎样?
这里还是强调一下,既然属性类型是可变类型,说明我们期望再程序中能够改变mutString的值,否则直接使用NSString了。

@property (nonatomic, copy) NSMutableString *mutString;

- (void)testStrCopy
{
    NSString *str = @"123";
    self.mutString = [NSMutableString stringWithString:str];
    NSLog(@"str = %p self.mutString = %p",str,self.mutString); // 两者的地址不一样
    [self.mutString appendString:@"456"]; // 会崩溃,因为此时self.mutArray是NSString类型,是不可变对象
}

执行程序后,会崩溃,崩溃原因是:

[NSTaggedPointerString appendString:]: unrecognized selector sent to instance 0xed877425eeef9883

self.mutString没有appendString方法。self.mutString是NSMutableString类型,为何没有appendString方法呢?这就是使用copy造成的。错误原因就在下面这行代码

self.mutString = [NSMutableString stringWithString:str];

这行代码到底发生了什么。这行代码实际上完成了两件事:

// 首先声明一个临时变量
NSMutableString *tempString = [NSMutableString stringWithString:str];
// 将该临时变量copy,赋值给self.mutString
self.mutString = [tempString copy];

注意,通过[tempString copy]得到的self.mutString是一个不可变对象,不可变对象自然没有appendString方法,这也是为何会崩溃的原因。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,175评论 1 32
  • 本文为转载: 作者:zyydeveloper 链接:http://www.jianshu.com/p/5f776a...
    Buddha_like阅读 950评论 0 2
  • gauge:to make a judgement or guess on a situation,action ...
    NancyJiang__阅读 176评论 0 0
  • 天天看着你们无聊的说笑 我也跟着附和 原本以为能融入你们的我 早就被你们甩了好远
    回首的背叛阅读 248评论 2 2
  • 我的2017年,今年我大学毕业,开始工作,进入社会,开始新的一种生活。 今年我考到护士证,人生多了一条退路,实现了...
    7元阅读 109评论 0 0