今天在人才孵化基地中,矫总提出了一个问题,然后根据此问题又深入的探索了一番,在此做个总结.
1.先说说用copy修饰
在这里举一个例子
@interface viewController()
@property (nonatomic, copy) NSString *name;//注意这里是用copy修饰的
@end
@implementation viewController
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableString *str = [NSMutableString stringWithString:@"张三"];
self.name = str;
NSLog(@"第一次得到的名字:%@",self.name);
[str appendString:@"丰"];
str = nil;
NSLog(@"第二次得到的名字:%@",self.name);
}
这里打印出来的结果是:
第一次得到的名字:张三
第二次得到的名字:张三
为什么会这样呢? 接下来告诉你
因为NSMutableString
是可变字符串,在这里 self.name = str;
name是用copy来修饰的,从而进行了一次深拷贝,而当接下来的[str appendString:@"丰"]; str = nil;
这些操作也就对self.name没有任何影响了.
那如果换成这样的话,结果会如何?
@interface viewController()
@property (nonatomic, copy) NSString *name;
@end
@implementation viewController
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableString *str = [NSMutableString stringWithString:@"张三"];
//把self.name换成了_name
_name = str;
NSLog(@"第一次得到的名字:%@",_name);
[str appendString:@"丰"];
str = nil;
NSLog(@"第二次得到的名字:%@",_name);
}
而这次打印出来的结果是:
第一次得到的名字:张三
第二次得到的名字:张三丰
因为使用_name没有去调用生成的set函数,所以copy修饰符不生效,从而进行了一次浅拷贝,而str = nil
这个操作为什么没有使得_name为空呢? 因为浅拷贝只是多了一个指向_name的指针,而str = nil
把指向str的指向的指针去掉了而已,但仍有一个有效的对象,所以在[str appendString:@"丰"];
这一步操作的时候就已经生效了.
2.接下来再说说用strong修饰
@interface viewController()
@property (nonatomic, strong) NSString *name;//注意这里是用strong修饰的
@end
@implementation viewController
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableString *str = [NSMutableString stringWithString:@"张三"];
self.name = str;
[str appendString:@"丰"];
NSLog(@"得到的名字:%@",self.name);
}
打印出来的结果是:
得到的名字:张三丰
这里要记住 如果使用strong修饰NSString类型属性,self.name 指向可变字符串对象的地址
当可变字符串内容发生变化时,self.name相对应的也发生变化,这次是进行了一次浅拷贝
但是像第一个例子那样,使用copy修饰的话,将可变字符串重新拷贝一份,重新开辟内存空间,修改mutableString的值,不会对self.name造成影响
刚才一直用的是可变字符串NSMutableString
,这次用不可变字符串NSString
再试试
@interface viewController()
@property (nonatomic, strong) NSString *name;
@end
@implementation viewController
- (void)viewDidLoad {
[super viewDidLoad];
NSString *str = [NSString stringWithString:@"张三"];
self.name = str;
NSLog(@"第一次得到的名字:%@",self.name);
str = @"丰";
NSLog(@"第二次得到的名字:%@",self.name);
}
打印出来的结果是:
第一次得到的名字:张三
第二次得到的名字:张三
将可变字符串变NSMutableString
为不可变字符串NSString
,在这里的赋值操作self.name = str;
,其实是让str重新指向了一片内存空间,并不是修改了str原本内存中的值,所以改变str的指向后,self.name的指向并没有改变,输出没有受到影响。
上面的例子差不多大家都应该明白了,但是有可能在实际操作中,会有一个问题困扰着大家,那就是 NSString到底是用copy还是strong?
在这里将解决你的烦恼:
本质上来讲,copy和strong都没有错,但不是说,NSString用copy就一定是最好的.
那么,什么时候用copy,什么时候用strong呢?
首先,为什么要用copy?
因为copy安全!
copy修饰的NSString,在初始化时,如果来源是NSMutableString的话,会对来源进行一次深拷贝,将来源的内存地址复制一份,这样两个对象就一点关系就没有了,无论你怎么操作来源,都不会对自己的NSString有任何影响
比如:
你有一个
@property(nonatomic,copy) NSString *str;
然后有一个NSMutableString *mutableStr;
当你进行str = mutableStr
操作之后,紧接着你又改变了mutableStr的内容mutableStr = @"change";
那么str的内容并不会改变. 如果你的str不是copy修饰的,而是strong修饰的,那么str的值也会变成@"change";
因为strong是浅拷贝的,并不会对来源的内存地址进行拷贝
可以结合上面的例子来理解
那么问题来了,既然copy安全,那为什么不都用copy?
这里我们需要了解一点,copy修饰的NSString在进行set操作时,底层是这样实现的:
我们还是举上面那个例子,进行str = mutableStr操作时,内部会执行一个操作:
str = [mutableStr copy];
那么这个copy里面做了什么呢?
if ([str isMemberOfClass:[str class]])
没错,就是进行一次判断,判断来源是可变的还是不可变的,如果是不可变,那么好,接下来的操作就跟strong修饰的没有区别,进行浅拷贝;如果是可变的,那么会进行一次深拷贝
所以,copy操作内部会进行判断,你别小看了这个if操作所消耗的内存,一次不重要,十次可能也可以忽略不计,但当你的项目十分庞大时,有成百上千个个NSString对象,多多少少会对你的app的性能造成一定的影响.
那么回到最初的问题,什么时候用copy,什么时候用strong
你只需要记住一点:
当你给你的NSString对象赋值时,如果来源是NSMutableString,那么这种情况就必须要用copy;如果你确定来源是不可变类型的,比如@"张三丰"
这种固定的字符串,那么用strong比较好