iOS理解“对象等同性”这一概念

前言

最近看了一道题
NSString *s1 = @"Hello world"; NSString *s2 = @"Hello world";请问 s1 == s2的返回值是YES还是NO?,
相信很多童鞋的答案都是NO,可能大家认为s1、s2两个对象的地址不同,但是事实真的如此么?

为此特意写了一个demo来印证下:


#importint main(int argc, const char * argv[]) {

@autoreleasepool {

NSString *str1 = @"abc";

NSString *str2 = [NSString stringWithFormat:@"%@",@"abc"];

NSString *str3 = [[NSString alloc]initWithString:str1];

NSString *str4 = [NSString stringWithString:str1];

NSString *str8 = [[NSString alloc] initWithString:@"abc"];

NSString *str9 = [NSString stringWithString:@"abc"];

NSString *str5 = [str1 copy];

NSString *str6 = [str1 mutableCopy];

NSString *str7 = [NSString stringWithFormat:@"%@",@"abc"];

NSLog(@"str1: %p",str1);

NSLog(@"str2: %p",str2);

NSLog(@"str3: %p",str3);

NSLog(@"str4: %p",str4);

NSLog(@"str5: %p",str5);

NSLog(@"str6: %p",str6);

NSLog(@"str7: %p",str7);

NSLog(@"str8: %p",str8);

NSLog(@"str9: %p",str9);

NSLog(@"===============");

NSLog(@"str1 == str2  \t\t%@",(str1 == str2) ? @"YES" : @"NO");

NSLog(@"str1 Equal str2  \t%@",[str1 isEqualToString:str2] ? @"YES" : @"NO");

NSLog(@"str1 == str3  \t\t%@",(str1 == str3) ? @"YES" : @"NO");

NSLog(@"str1 == str4  \t\t%@",(str1 == str4) ? @"YES" : @"NO");

NSLog(@"str3 == str4  \t\t%@",(str4 == str3) ? @"YES" : @"NO");

NSLog(@"str2 == str6  \t\t%@",(str2 == str6) ? @"YES" : @"NO");

NSLog(@"str2 Equal str6  \t%@",[str2 isEqualToString:str6] ? @"YES" : @"NO");

NSLog(@"str2 == str7  \t\t%@",(str7 == str2) ? @"YES" : @"NO");

NSLog(@"%d",str2 == str7);

}

return 0;

}

//LOGCAT如下

2017-02-28 13:27:45.326 testEqual[6456:440040] str1: 0x100001048

2017-02-28 13:27:46.022 testEqual[6456:440040] str2: 0x63626135

2017-02-28 13:27:46.765 testEqual[6456:440040] str3: 0x100001048

2017-02-28 13:27:47.284 testEqual[6456:440040] str4: 0x100001048

2017-02-28 13:27:47.732 testEqual[6456:440040] str5: 0x100001048

2017-02-28 13:27:48.835 testEqual[6456:440040] str6: 0x100102370

2017-02-28 13:27:49.690 testEqual[6456:440040] str7: 0x63626135

2017-02-28 13:28:05.204 testEqual[6456:440040] str8: 0x100001048

2017-02-28 13:28:16.683 testEqual[6456:440040] str9: 0x100001048

2017-02-28 13:29:17.041 testEqual[6456:440040] ===============

2017-02-28 13:29:18.178 testEqual[6456:440040] str1 == str2  NO

2017-02-28 13:29:21.152 testEqual[6456:440040] str1 Equal str2  YES

2017-02-28 13:31:01.274 testEqual[6456:440040] str1 == str3  YES

2017-02-28 13:31:01.274 testEqual[6456:440040] str1 == str4  YES

2017-02-28 13:31:02.918 testEqual[6456:440040] str3 == str4  YES

2017-02-28 13:31:02.918 testEqual[6456:440040] str2 == str6  NO

2017-02-28 13:31:02.918 testEqual[6456:440040] str2 Equal str6  YES

2017-02-28 13:31:02.918 testEqual[6456:440040] str2 == str7  YES

2017-02-28 13:31:02.919 testEqual[6456:440040] 1

由此可以得出结论

Tips:

  1. str1 是 直接使用字面量语法赋值的变量。所以,跟str8,str9是一样的,编译器都会执行同样的代码来生成对象。

  2. str3和str4,则都是根据一个已知的字符串变量来生成对象。其实就是比上面Tip1多了一个步骤、所以,str3和str8,str4和str9地址是相同的

  3. str2和str7[NSString stringWithFormat:@"%@",@"abc"]都是格式化生成,地址也是相同的

扩展:NSString *str10 = [NSString stringWithFormat:@"%@",str1];同理,如果新加一个str10,那它的地址应该跟str2,str7也是一样的

  1. 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对象copystrong修饰词的区别

//先声明两个变量
@property (nonatomic, strong) NSString *strongStr;
@property (nonatomic, copy) NSString *cpyStr;

然后这里分两种情况

  1. 不可变str的情况
- (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的值意味着改变指针的指向
此时,copystrong修饰的新字符串值都不会变!因为他们的指针指向的地址都还是以前的地址!!

  1. 可变Str的情况
- (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生成对象地址和别的方法生成对象地址不同的问题:

由上文可以看到,stringWithFormatinitWithFormat两方法生成的NSString对象地址是相同的。其他的任何方法生成的对象都不同!因为stringWithFormat方法内部也是调用了initWithFormat方法。


原因是这样:
stringWithFormat方法会创建一个新的对象,开辟新的内存空间,遵循引用计数原则,创建释放等等。
stringWithString等方法,其实并没有创建一个新的对象,苹果对于这种字面量字符串,一般都是存储在常量区stringWithFormat等方法只是添加一个该常量的引用而已,并不会开辟新的内存空间

这也就能解释为什么这两种方式创建的字符串,内存地址不同了

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容

  • NSRange : 表示范围作用的结构体,3种方式创建 // 方式一 NSRange range; locatio...
    路墨阅读 998评论 1 8
  • java笔记第一天 == 和 equals ==比较的比较的是两个变量的值是否相等,对于引用型变量表示的是两个变量...
    jmychou阅读 1,483评论 0 3
  • 1、禁止手机睡眠[UIApplication sharedApplication].idleTimerDisabl...
    DingGa阅读 1,114评论 1 6
  • 我想可能很多人在恋爱中都像我一样:一牵手就在心中规划好了一生。 在恋爱中总是会问对方,你想过我们要有以后吗?以前曾...
    轻情阅读 307评论 0 0
  • liunx命令总共常用只有80个,总共600个 liunx和DOS,window不同是,只要文件有可执行权限,不管...
    stillriver阅读 421评论 0 0