惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

aimingoo的专栏
aimingoo的专栏
Cyberwarzone
Cyberwarzone
博客园_首页
爱范儿
爱范儿
腾讯CDC
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
K
Kaspersky official blog
H
Help Net Security
The GitHub Blog
The GitHub Blog
G
Google Developers Blog
S
SegmentFault 最新的问题
L
LINUX DO - 热门话题
T
Tenable Blog
P
Privacy & Cybersecurity Law Blog
N
News | PayPal Newsroom
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
L
LangChain Blog
L
Lohrmann on Cybersecurity
P
Palo Alto Networks Blog
云风的 BLOG
云风的 BLOG
A
Arctic Wolf
N
News and Events Feed by Topic
AWS News Blog
AWS News Blog
美团技术团队
U
Unit 42
月光博客
月光博客
阮一峰的网络日志
阮一峰的网络日志
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
S
Secure Thoughts
有赞技术团队
有赞技术团队
C
Cyber Attacks, Cyber Crime and Cyber Security
Schneier on Security
Schneier on Security
Cloudbric
Cloudbric
B
Blog
NISL@THU
NISL@THU
Help Net Security
Help Net Security
Y
Y Combinator Blog
J
Java Code Geeks
S
Securelist
宝玉的分享
宝玉的分享
T
Threat Research - Cisco Blogs
S
Security @ Cisco Blogs
O
OpenAI News
D
DataBreaches.Net
Know Your Adversary
Know Your Adversary
Hacker News - Newest:
Hacker News - Newest: "LLM"
Vercel News
Vercel News
Forbes - Security
Forbes - Security
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed

土土哥的Blog

好久没有更新博客=。= 阿里巴巴国际无线技术部 - 招人啦~求iOS、Android、Java 转-阿里巴巴国际无线技术部 - 在这里遇见最好的自己 反编译分析并模拟实现methodSignatureForSelector方法 反编译分析Xcode8的Bug, release下连续两次调用有二级指针参数的空方法会Crash 有趣的Autolayout示例5-Masonry实现 在对象dealloc的后期执行Task-开源库TTGDeallocTaskHelper 用QuartzCode快速实现一个收藏动画 开源项目-拼图验证控件TTGPuzzleVerify的实现 Swift开源Mac App - BingWallPaper 有趣的Autolayout示例4-Masonry实现 翻译-为什么objc_msgSend必须用汇编实现 API返回结果设计经验与总结 结合访问Out Parameters出现EXC_BAD_ACCESS的例子,反编译汇编解读__autoreleasing 总结一些iOS项目中组织代码的方法 对组件化与模块化的思考与总结 开源项目-TTGTagCollectionView 有趣的Autolayout示例3-Masonry实现 Swift开源项目: TTGEmojiRate的实现 Swift写的库-TTGSnackbar 有趣的Autolayout示例2-Masonry实现 解决iOS项目的版本兼容问题-结合宏、Category和Runtime 用Runtime的手段填充任意NSObject对象的nil属性 有趣的Autolayout示例-Masonry实现 UITextView编辑时插入自定义表情-续-自定义表情图片的大小 RPC框架Thrift例子-PHP调用C++后端程序 GCD使用经验与技巧浅谈 为GCD队列绑定NSObject类型上下文数据-利用__bridge_retained(transfer)转移内存管理权 Enum-枚举的正确使用-Effective-Objective-C-读书笔记-Item-5 @autoreleasepool-内存的分配与释放 有关宏定义的经验与技巧-简化代码-增强Log Effective-Objective-C-读书笔记-Item-4-如何正确定义常量 UITextView编辑时插入自定义表情-简单的图文混编 关于评论不见了=。= Entity和Model的不同-关于代码的数据层 一次审核被拒的经历-关于iCloud到底应该备份什么数据 Block类型变量-缓存Http请求与回调 提升UITableView性能-复杂页面的优化 利用NSProxy实现消息转发-模块化的网络接口层设计-原创 Effective-Objective-C-读书笔记-Item-3 Effective-Objective-C-读书笔记-Item-2 Effective-Objective-C-读书笔记-Item-1 iOS项目的目录结构-原创 Android开源库-LinkTextView-原创 第一篇Blog
NSString的Copy与内存分配
土土哥 · 2015-02-18 · via 土土哥的Blog

前言

不管是用什么语言开发,字符串应该算是用的最多的了。在Objective-C中,字符串类是NSString,以及其子类NSMutableString等。所以,对于字符串类,编程语言、编译器等往往会做特殊处理。下面我就说说我自己在项目中发现的NSString的“奇怪”的地方。

字符串常量,copy,mutableCopy

Example

先看看如下面的代码,猜猜会输出什么:

1
2
3
4
5
6
7
8
9
10
11
12
NSString *str1 = @"abc";
NSString *str2 = @"abc";
NSString *str3 = [[NSString alloc] initWithString:@"abc"];
NSString *str4 = [str1 copy];
NSString *str5 = [str1 mutableCopy];


NSLog(@"str1: %p", str1);
NSLog(@"str2: %p", str2);
NSLog(@"str3: %p", str3);
NSLog(@"str4: %p", str4);
NSLog(@"str5: %p", str5);

直接给出结果:

1
2
3
4
5
6

str1: 0x10cd12040
str2: 0x10cd12040
str3: 0x10cd12040
str4: 0x10cd12040
str5: 0x7f874b508350

看,是不是很“神奇”。

简单解释

NSString是“不可变(immutable)”的类型,这个是关键。
代码中的“@”开头的字符串,其实就是一个字符串常量,运行时会检测这个字符串是否已经存在,存在的话,就直接将这个字符串的地址赋给变量;不存在的话,则创建,再赋值。

第一行: “abc”字符串不存在,创建,取得字符串地址“0x10cd12040”,赋给str1.

第二行: “abc”字符串已经存在,直接将地址“0x10cd12040”赋给str2.

第三行: (记住,NSString是不可变的)alloc并不会分配新的内存,直接将字符串地址“0x10cd12040”赋给str3.

第四行: copy,既然是不可变的,也就没有必要分配新内存,直接将地址“0x10cd12040”赋给str4.

第五行: mutableCopy其实就是创建一个NSMutableString的实例,既然是“可变(mutable)”的类型了,当然要分配新的内存,所以,字符串的地址也变成新的“0x7f874b508350”。

可以这么说,编译器、runtime充分利用了NSString是“不可变(immutable)”类型这个特点,只要字符串内容一致,就不会分配新的内存储存,字符串在程序中使用量非常大,这么做无疑可以大大节省内存,提升性能。

Property 和 NSString

先来看看一个UserEntity类:

1
2
3
4
5


@interface UserEntity : NSObject <NSCopying>
@property(strong, nonatomic) NSString *name;
@end

只有一个名为name的NSString类型Property。
相信大家对这个不陌生吧。一般我们的应用中会有很多Entity类型,每个Entity类里面肯定避免不了有许多NSString的字符串类型。这里的name的属性是:strong和nonatomic,strong表明name要“持有”一份name所指的字符串的引用,nonatomic表示property类型不用加锁。

接着咱们看看下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
UserEntity *userEntity = [UserEntity new];


NSMutableString *showName = [[NSMutableString alloc] initWithString:@"tutuge"];


userEntity.name = showName;


[showName appendString:@" blog"];


NSLog(@"Name: %@", userEntity.name);

猜猜会输出什么?

1
Name: tutuge blog

看,我们明明是保存的“tutuge”,怎么到后面输出的是“tutuge blog”?

让NSString类型的Property为Copy型

因为name的Property属性是strong的,所以赋值后,它跟showName均指向同一个NSMutableString实例,如下:

内存示意图

所以改变了showName,当然name输出的值也就变了。
这个时候,name的property属性就不能是strong了,应该是copy,如下:

1
2
3
4
5


@interface UserEntity : NSObject <NSCopying>
@property(copy, nonatomic) NSString *name;
@end

这样的话,当“userEntity.name = showName;”这句运行后,内存布局如下:

内存示意图

这样,就避免了这个问题。

总结

很所时候,代码并不是看上去那样“运行”的,多试试,多查阅资料,就能了解其中的奥秘了~