欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

NSString用copy, NSMutableString用strong.

程序员文章站 2024-01-15 08:41:52
...

上代码:

//定义两个属性:
/** strong修饰的字符串 **/
@property (nonatomic, strong) NSString *sString;
/** copy修饰的字符串 **/
@property (nonatomic, copy) NSString *cString;

- (void)test {
    NSMutableString *mString = [[NSMutableString alloc] initWithFormat:@"我是可变的字符串"];
    
    //进行赋值
    self.sString = mString;
    self.cString = mString;
    
    NSLog(@"\n mString: %@, %p, %p \n sString: %@, %p, %p \n cString: %@, %p, %p", mString, mString, &mString, self.sString, _sString, &_sString, self.cString, _cString, &_cString);
    
    //打印结果(指针所指向对象的内存地址,指针内存地址)
    /**
     mString: 我是可变的字符串, 0x60000386d020, 0x7ffeebc11a70
     sString: 我是可变的字符串, 0x60000386d020, 0x7f9f2e913830
     cString: 我是可变的字符串, 0x60000386d590, 0x7f9f2e913838
     */
    
    //追加字符串
    [mString appendString:@"我是追加的字符串"];
    
    NSLog(@"\n mString: %@, %p, %p \n sString: %@, %p, %p \n cString: %@, %p, %p", mString, mString, &mString, self.sString, _sString, &_sString, self.cString, _cString, &_cString);
    
    //打印结果(指针所指向对象的内存地址,指针内存地址)
    /**
     mString: 我是可变的字符串我是追加的字符串, 0x60000386d020, 0x7ffeebc11a70
     sString: 我是可变的字符串我是追加的字符串, 0x60000386d020, 0x7f9f2e913830
     cString: 我是可变的字符串, 0x60000386d590, 0x7f9f2e913838
     */
}

分析:

  • strong修饰的sString指向对象的内存地址和mString指向对象内存地址是一样的, 就是说sString和mString指向的是同一个对象@“我是可变的字符串”,这个对象的地址没有变化,所以它们的值是一样的.
  • 而copy修饰的cString指向对象的内存地址却和它们不同,因为当把mString赋值给copy的cString时,cString对象是深拷贝而来,一个新的对象,这个对象有新的地址不再是原来的地址了.也就是开辟了新的内存地址.
  • 从输出结果看,当mString追加新的内容后,cString的内容没有发生改变. sString的内容发生了改变.因为strong是浅拷贝(指针拷贝),而copy是深拷贝,开辟了新的内存地址,这样更安全,特别是NSArray和NSDictionary,如果值被莫名其妙的改变了,那就只能gg了.

1.如果用不可变得字符串进行赋值,会发生什么呢?
上代码:

- (void)test {
    NSString *string = [[NSString alloc] initWithFormat:@"我是不可变的字符串"];
    
    //进行赋值
    self.sString = string;
    self.cString = string;
    
    NSLog(@"\n string: %p, %p \n sString: %p, %p \n cString: %p, %p", string, &string, _sString, &_sString, _cString, &_cString);
    
    //打印结果(指针所指向对象的内存地址,指针内存地址)
    /**
     string: 0x6000027c1e90, 0x7ffee934ea58
     sString: 0x6000027c1e90, 0x7f89c6d0cdc0
     cString: 0x6000027c1e90, 0x7f89c6d0cdc8
     */
}

分析:

  • 可以看到,无论是strong还是copy,在这里都是进行了浅拷贝,不会重新开辟新的空间.因为被赋值的对象本身string是不可变的,有一定的安全性,那么那些后来进行copy,strong的也同样不会影响安全性,而且内存管理也一样.

2.自定义类如何让它具有copy功能?
遵守NScoping协议,实现copywithzone方法即可.
代码:

 -  (id)copyWithZone:(NSZone *)zone {
         Student *student = [[Student allocWithZone:zone] initWithName: self.name age:self.age];
         return student;
}

分析:

  • 浅拷贝:指针赋值,使两个指针指向相同的一块内存空间,操作不安全。
  • Foundation类已经遵守了和 协议,即实现了copy和mutableCopy方法,因此Foundation对象可以使用这些方法创建对象的副本或可变副本

3.用copy和strong关键字的原因分析.

  1. 一般情况下,我们不希望我们创建的字符串跟着mStr变化而变化,故用copy.如果希望字串的值跟着mStr变化,可以使用strong,retain来修饰.
  2. 通常情况下,因为父类指针可以指向子类对象,使用copy的目的是为了让本对象的属性不受外界影响,使用copy来修饰无论给我传入是一个可变对象还是不可对象,我本身持有的就是一个不可变的副本,这样更安全.
  3. <1>.总结: 对于用NSMutableString的字符串来赋值,strong声明的属性不管是可变的还是不可变得,都会浅拷贝,只拷贝指针地址,会随着mString的改边而改变.而copy声明的属性不管是可变的还是不可变得,都会深拷贝,堆区开辟新的空间,不会随着mString的改变而改变.
    <2>.对于用NSString的字符串来赋值,不管是strong声明的属性还是copy声明的属性,也不管他们各自是可变的还是不可变得,都会浅拷贝,此时copy也是浅拷贝,不会在堆区开辟新空间.
    <3>说白了就是一个安全的问题,声明一个NSString *str变量,然后把一个NSMutableString *mStr变量的赋值给它了如果要求str跟着mStr变化那么就用strong;如果str不能跟着mStr一起变化那就用copy而对于要把NSString类型的字符串赋值给str,那两都没啥区别。
  4. –>copy此特质所表达的所属关系与strong类似。
    –>然而设置方法并不保留新值,而是将其“拷贝” (copy)。
    –>当属性类型为NSString时,经常用此特质来保护其封装性,因为传递给设置方法的新值有可能指向一个NSMutableString类的实例。
    –>这个类是NSString的子类,表示一种可修改其值的字符串,此时若是不拷贝字符串,那么设置完属性之后,字符串的值就可能会在对象不知情的情况下遭人更改。
    –>所以,这时就要拷贝一份“不可变” (immutable)的字符串,确保对象中的字符串值不会无意间变动。
    –>只要实现属性所用的对象是“可变的” (mutable),就应该在设置新属性值时拷贝一份。
  5. 浅拷贝 — 只拷贝对象地址,不会拷贝内容,不会开辟新的空间.
    深拷贝 — 拷贝内容,堆区开辟新的空间,拷贝出的内容是可变的.