细说@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方法,这也是为何会崩溃的原因。

猜你喜欢

转载自blog.csdn.net/weixin_34033624/article/details/86988242