前言
最近看了一道题NSString *s1 = @"Hello world"; NSString *s2 = @"Hello world";请问 s1 == s2的返回值是YES还是NO?
,
相信很多童鞋的答案都是NO,可能大家认为s1、s2两个对象的地址不同,但是事实真的如此么?
1 |
|
由此可以得出结论
Tips:
str1 是 直接使用字面量语法赋值的变量。所以,跟str8,str9是一样的,编译器都会执行同样的代码来生成对象。
str3和str4,则都是根据一个已知的字符串变量来生成对象。其实就是比上面Tip1多了一个步骤、所以,
str3和str8,str4和str9地址是相同的
str2和str7
[NSString stringWithFormat:@"%@",@"abc"]
都是格式化生成,地址也是相同的
扩展:NSString *str10 = [NSString stringWithFormat:@"%@",str1];
同理,如果新加一个str10,那它的地址应该跟str2,str7也是一样的
- str5
[str1 copy]
和str6[str1 mutableCopy]
这两个涉及到深浅拷贝,
str5是str1的浅拷贝,指针都指向相同的地址
str6是str1的深拷贝,等于重新创建了一个新的对象所以地址是不同的
因此
如果上面的问题换种问法,比如:
`NSString *s1 = @”hello”;
NSString *s2 = [[NSString alloc] initWithString:s1];
请问s1==s2的返回值?`
或者
`NSString *s1 = [NSString stringWithFormat:@”hello”];
NSString *s2 = [NSString stringWithFormat:@”hello”];
请问s1==s2的返回值?`
答案也都是YES!
PS:
单说NSString,系统为我们提供了一个isEqualToString:
方法,所以一般情况下来说,我们是不会使用==
来判断两个NSString对象是否相等的。
==
和isEqualToString:
有什么区别呢?
由上面的例子也可以看到,
`str1 == str2 NO
str1 Equal str2 YES`
isEqualToString:
应该是只比较了两个对象的值,不比较地址
而==
则会比较两个对象的地址和值是否都相等
毕竟判断两个对象是否相等的条件是:
当且仅当其“指针值(pointer value)”完全相等时,这两个对象才相等
.
2018年11月26日延伸:
一、NSString对象copy
和strong
修饰词的区别
1 | //先声明两个变量 |
然后这里分两种情况
- 不可变str的情况
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22- (void)inmutableTest {
NSString *string = [NSString stringWithFormat:@"abc"];
self.strongStr = string;
self.cpyStr = string;
NSLog(@"string:%@, 对象地址:%p, 指针地址:%p", string,string,&string);
NSLog(@"strongStr:%@, 对象地址:%p, 指针地址:%p,", self.strongStr,_strongStr,&_strongStr);
NSLog(@"cpyStr:%@, 对象地址:%p, 指针地址:%p", self.cpyStr,_cpyStr,&_cpyStr);
//改变string的值
string = @"123";
NSLog(@"string:%@, 对象地址:%p, 指针地址:%p", string,string,&string);
NSLog(@"strongStr:%@, 对象地址:%p, 指针地址:%p,", self.strongStr,_strongStr,&_strongStr);
NSLog(@"cpyStr:%@, 对象地址:%p, 指针地址:%p", self.cpyStr,_cpyStr,&_cpyStr);
}
//结果:
//string:abc, 对象地址:0xe9f9b161ce4bbd88, 指针地址:0x7ffee1306e48
//strongStr:abc, 对象地址:0xe9f9b161ce4bbd88, 指针地址:0x7fcc7b42a660,
//cpyStr:abc, 对象地址:0xe9f9b161ce4bbd88, 指针地址:0x7fcc7b42a668
//string:123, 对象地址:0x10e8fa1a0, 指针地址:0x7ffee1306e48
//strongStr:abc, 对象地址:0xe9f9b161ce4bbd88, 指针地址:0x7fcc7b42a660,
//cpyStr:abc, 对象地址:0xe9f9b161ce4bbd88, 指针地址:0x7fcc7b42a668
可以看到,在string为NSString(不可变)的情况下,修改string的值意味着改变指针的指向
此时,copy
和strong
修饰的新字符串值都不会变!因为他们的指针指向的地址都还是以前的地址!!
- 可变Str的情况
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22- (void)mutableTest {
NSMutableString *string= [[NSMutableString alloc]initWithString:@"abc"];
self.strongStr = string;
self.cpyStr = string;
NSLog(@"string:%@, 对象地址:%p, 指针地址:%p", string,string,&string);
NSLog(@"strongStr:%@, 对象地址:%p, 指针地址:%p,", self.strongStr,_strongStr,&_strongStr);
NSLog(@"cpyStr:%@, 对象地址:%p, 指针地址:%p", self.cpyStr,_cpyStr,&_cpyStr);
//改变string的值
[string appendFormat:@"%@",@"123"];
NSLog(@"string:%@, 对象地址:%p, 指针地址:%p", string,string,&string);
NSLog(@"strongStr:%@, 对象地址:%p, 指针地址:%p,", self.strongStr,_strongStr,&_strongStr);
NSLog(@"cpyStr:%@, 对象地址:%p, 指针地址:%p", self.cpyStr,_cpyStr,&_cpyStr);
}
//结果:
//string:abc, 对象地址:0x60000094ffc0, 指针地址:0x7ffee2898758
//strongStr:abc, 对象地址:0x60000094ffc0, 指针地址:0x7f998c70d520,
//cpyStr:abc, 对象地址:0x9b661481bbd71a70, 指针地址:0x7f998c70d528
//string:abc123, 对象地址:0x60000094ffc0, 指针地址:0x7ffee2898758
//strongStr:abc123, 对象地址:0x60000094ffc0, 指针地址:0x7f998c70d520,
//cpyStr:abc, 对象地址:0x9b661481bbd71a70, 指针地址:0x7f998c70d528
此结果就能明显看到区别了,strong
修饰的字符串,会跟随源字符串的变化而变化!copy
修饰的并不会!!
这也可以解释:为什么NSString的变量要用copy
来修饰
生成NSString对象的时候,就是不想在后续的过程中改变他,如果使用strong
来修饰,则途中修改了原值,strong修饰的变量也会跟着变化,这显然有悖于初衷
二、有关原文中stringWithFormat生成对象地址和别的方法生成对象地址不同的问题:
由上文可以看到,
stringWithFormat
和initWithFormat
两方法生成的NSString对象地址是相同的。其他的任何方法生成的对象都不同!因为stringWithFormat
方法内部也是调用了initWithFormat
方法。
原因是这样:stringWithFormat
方法会创建一个新的对象,开辟新的内存空间,遵循引用计数原则,创建释放等等。stringWithString
等方法,其实并没有创建一个新的对象,苹果对于这种字面量字符串,一般都是存储在常量区
,stringWithFormat
等方法只是添加一个该常量的引用而已,并不会开辟新的内存空间
这也就能解释为什么这两种方式创建的字符串,内存地址不同了