copy与mutableCopy的内存管理剖析-阿里云开发者社区

开发者社区> niuxiaoxiang> 正文

copy与mutableCopy的内存管理剖析

简介:
+关注继续查看

title: copy与mutableCopy的内存管理剖析
date: 2016-04-24 16:50:04

tags: copy

copy与mutableCopy相关的内存管理

不知道为什么一说这个,很多人都拿NSString和NSMutableString做测试,我想最直接的是因为常用又实现了copy和mutableCopy的代理,但是NSString类的其实比较特殊,不应该拿NSString来测试,所以本文拿实现代理的NSURLRequest来做测试。

immutableObject:

非集合的

NSURL *url = [[NSURL alloc] initWithString:@"ceshi"];
NSURLRequest *urlRequest = [[NSURLRequest alloc] initWithURL:url];
NSLog(@"urlReqeust:%@, urlRequestRec:%ld", urlRequest, [urlRequest retainCount]);
//log: urlReqeust: { URL: ceshi }, urlRequestRec:1

NSURLRequest *urlRequest1 = [urlRequest copy];
NSLog(@"urlRequest1:%@, urlRequest1:%ld", urlRequest1, [urlRequest1 retainCount]);
// log: urlRequest1: { URL: ceshi }, urlRequest1:2

总结:immutableObject做copy时,做的是浅copy,也就是说只是所指向的对象的内存计数器增加了1,所以如果urlRequest1重新赋值时,需要做下release把之前所指的对象的内存计数器减1,否则就会有内存泄露。如下:

[urlRequest1 release], urlRequest1 = nil;
// 此时urlRequest所指对象计数器为1,urlRequest1为nil
urlRequest1 = [NSURLRequest requestWithURL:[NSURL URLWithString:@"ceshi2"]];
// 此时urlRequest1指向新的对象,指向的对象的计数器为1
集合的

NSArray *array = @[@[@"a", @"b"], @[@"c", @"d"]];
NSArray *copyArray = [array copy];
NSMutableArray *mCopyArray = [array mutableCopy];
// log: =====array:0x7fb2a8f0d2b0,arrayRec:2, =====copyArray:0x7fb2a8f0d2b0,copyArrayRec:2, ======mCopyArry:0x7fb2a8e10ef0, mCopyArrayRec:1

// 有bug,必须要做copyArray的release

copyArray = @[@"a",@"d"];
NSLog(@"array:%@,copyArr:%@,mCopyArray:%@", array, copyArray, mCopyArray);
// log: =====array:0x7fb2a8f0d2b0,arrayRec:2, =====copyArray:0x7fb2a8f1b980,copyArrayRec:1, ======mCopyArry:0x7fb2a8e10ef0, mCopyArrayRec:1

与immutable的非集合一样,copy将直接增加指针指向对象的引用计数器(集合的话:各元素的指针计数器都增加了1)。所以在copyArray更改前,必须要做release,否则会有内存泄漏

mutableObject

非集合的

    NSURL *url = [[NSURL alloc] initWithString:@"ceshi"];
NSURLRequest *urlRequest = [[NSURLRequest alloc] initWithURL:url];
NSLog(@"urlReqeust:%@, urlRequestRec:%ld\r", urlRequest, [urlRequest retainCount]);
// log: urlReqeust:<NSURLRequest: 0x7f98dac07e00> { URL: ceshi }, urlRequestRec:1

NSMutableURLRequest *mutableRequest = [urlRequest mutableCopy];
NSLog(@"mutableRequest:%@, mutableRequest:%ld", mutableRequest, [mutableRequest retainCount]);
// log:  mutableRequest:<NSMutableURLRequest: 0x7f98daf07390> { URL: ceshi }, mutableRequest:1

NSMutableURLRequest *mutableRequest1 = [mutableRequest mutableCopy];
NSLog(@"mutableRequest1:%@, mutableRequest1:%ld", mutableRequest1, [mutableRequest1 retainCount]);
// log: mutableRequest1:<NSMutableURLRequest: 0x7f98dac674f0> { URL: ceshi }, mutableRequest1:1

集合的


NSMutableArray *array = [NSMutableArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c",nil];
NSArray *copyArray = [array copy];
NSMutableArray *mCopyArray = [array mutableCopy];
// log: =====array:0x7fbbd3d0d730,arrayRec:1, =====copyArray:0x7fbbd3d19610,copyArrayRec:1, ======mCopyArry:0x7fbbd3d18ad0, mCopyArrayRec:1

总结:mutableObject的复制是深复制,直接创建一个新的内存地址来复制其内容(在集合中把各个元素的内容都赋值了一遍)。 与之前的被复制项无任何关系。

汇总:

  • [immutableObject copy] // 浅复制
  • [immutableObject mutableCopy] //深复制
  • [mutableObject copy] //深复制
  • [mutableObject mutableCopy] //深复制

特殊的NSString


NSString *str1 = @"abc";
NSString *str2 = @"abc";
NSString *str3 = [[NSString alloc] initWithString:@"abc"];
NSString *str4 = [str1 copy];
NSString *str5 = [[NSString alloc] initWithString:@"abc"];
NSString *str6 = [str1 mutableCopy];

//输出内存中的地址
NSLog(@"str1: %p", str1);
NSLog(@"str2: %p", str2);
NSLog(@"str3: %p", str3);
NSLog(@"str4: %p", str4);
NSLog(@"str5: %p", str5);
NSLog(@"str6: %p", str6);

猜下结果:

2016-04-24 22:41:50.629 TestCopy[2094:229125] str1: 0x1081af070
2016-04-24 22:41:50.630 TestCopy[2094:229125] str2: 0x1081af070
2016-04-24 22:41:50.630 TestCopy[2094:229125] str3: 0x1081af070
2016-04-24 22:41:50.630 TestCopy[2094:229125] str4: 0x1081af070
2016-04-24 22:41:50.630 TestCopy[2094:229125] str5: 0x1081af070
2016-04-24 22:41:50.631 TestCopy[2094:229125] str6: 0x7fb1d3f0ce70

我的理解:
NSString在程序中使用非常大,所以在immutable的copy和mutableCopy的原理下,为了系统性能的考虑,增加了只要是NSString存在,则直接把字符串地址付给使用者即可。如果此时str2=@"abdt"了,则直接拿生成的abdt的地址赋值给str2即可。

NSMutableString与immutableObj类似


NSMutableString *str1 = [NSMutableString stringWithFormat:@"abc"];
NSMutableString *str2 = [[NSMutableString alloc] initWithString:@"abc"];
NSMutableString *str3 = [str1 copy];
NSMutableString *str4 = [str1 mutableCopy];

//输出内存中的地址
NSLog(@"str1: %p, reC:%ld", str1, [str1 retainCount]);
NSLog(@"str2: %p, reC:%ld", str2, [str2 retainCount]);
NSLog(@"str3: %p", str3);
NSLog(@"str4: %p", str4);

log:
2016-04-24 22:58:42.319 TestCopy[2164:240308] str1: 0x7fa7ebca0c50, reC:1
2016-04-24 22:58:42.319 TestCopy[2164:240308] str2: 0x7fa7ebca8560, reC:1
2016-04-24 22:58:42.319 TestCopy[2164:240308] str3: 0xa000000006362613
2016-04-24 22:58:42.320 TestCopy[2164:240308] str4: 0x7fa7ebca7ed0

最后看2段代码:


//UserEntity.h
@interface UserEntity : NSObject
@property(strong, nonatomic) NSString *name;
@end
UserEntity *userEntity = [UserEntity new];
//创建mutable类型的字符串
NSMutableString *showName = [[NSMutableString alloc] initWithString:@"tutuge"];
//先保存“tutuge”字符串到userEntity的name
userEntity.name = showName;
//修改showName
[showName appendString:@" blog"];
//输出userEntity的name属性
NSLog(@"Name: %@", userEntity.name);

输出为:tutuge blog

如果更改为:@interface UserEntity : NSObject
@property(copy, nonatomic) NSString *name;
@end

则输出为: tutuge

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
Tomcat 6 --- 你很少使用的安全管理SecurityManager
试想一下,如果你的JSP页面中包含一句代码“System.exit(1);”,你的web应用访问到该JSP时,会发生什么? 一般使用tomcat可能都没有注意到这个问题,本篇主要讲述tomcat 6中SecurityManager的管理机制,尽量使用简单明了的图片表示其中关系。
943 0
SpringBoot2 整合 SpringSecurity 框架,实现用户权限安全管理
本文源码:GitHub·点这里 || GitEE·点这里 一、Security简介 1、基础概念 Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。
15149 0
Android 6.0 fork Zygote时的存储权限管理
Android 6.0开始支持运行时权限管理的功能。运行时权限管量中当然也包括对READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE这两个权限的动态管理。系统需要提供在不杀掉或重启已经运行的应用的情况下去动态授权的机制。 这篇文章就是讲在Zygote fork的
7668 0
使用OpenApi弹性释放和设置云服务器ECS释放
云服务器ECS的一个重要特性就是按需创建资源。您可以在业务高峰期按需弹性的自定义规则进行资源创建,在完成业务计算的时候释放资源。本篇将提供几个Tips帮助您更加容易和自动化的完成云服务器的释放和弹性设置。
11956 0
InnoDB的后台线程(IO线程,master线程,锁监控线程,错误监控线程)和内存(缓冲池,重做日志缓冲池,额外内存池)
InnoDB有多个内存块,你可以认为这些内存块组成了一个大的内存池,负责如下工作: 维护所有进程/线程需要访问的多个内部数据结构。 缓存磁盘上的数据,方便快速地读取,并且在对磁盘文件的数据进行修改之前在这里缓存。
814 0
+关注
8
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载