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

上一篇:WINDBG分析CrashDump配置


下一篇:某银行无线网络频繁掉线重认证分析、解决方案及抓包经验分享