首先看一段很简单的代码:
NSString *urlStr = @"http://120.25.226.186:32812/resources/videos/minion_10.mp4"; NSURL *url = [NSURL URLWithString:urlStr]; NSMutableDictionary *dict = [NSMutableDictionary dictionary]; dict[url] = @"ZYCoderr"; while (1) { NSString *value = dict[url]; }
运行后点击Xcode的Debug Session来看模拟器的状态:
CPU占用为99%。但我想问的问题不是关于CPU的, 而是关于内存的。接下来我稍微改变一下上面的代码:
NSString *urlStr = @"http://120.25.226.186:32812/resources/videos/minion_10.mp4"; NSURL *url = [NSURL URLWithString:urlStr]; NSMutableDictionary *dict = [NSMutableDictionary dictionary]; dict[url.absoluteString] = @"ZYCoderr"; while (1) { NSString *value = dict[url.absoluteString]; }
我仅仅是改变了字典的key。再运行一遍:
这是程序运行10s后的样子。我第一次注意到这个问题的时候内存已经飙到了9.9G, 鼠标几乎都快移不动了(麻麻, 编程好可怕……)
经过我多次尝试, 发现问题只出在第6行这一行身上面。当你将url.absoluteString作为你用来取值的key使用的时候, 就会发生内存暴增的问题。 就算你采取下面的写法:
NSString *key = url.absoluteString; NSString *value = dict[key];
情况仍然不可避免。但如果你直接采用下面的写法:
NSString *value = dict[urlStr];
你会发现问题又消失了。
另外, 你可能会在while循环中打一个log,然后发现内存暴增的情况又“消失”了。但事实上并不是这样, 这只是while循环运行速度减慢了而已, 内存仍在稳步增长。
虽然不使用url.absoluteString当做字典的key确实可以避免这个问题, 但我还是很好奇, 到底是什么原因造成了内存的暴增?
url.absoluteString会持续的创建新的实例,系统释放内存跟不上,打印Log的时候,系统会有一点时间去释放内存,所有内存增加稍微减缓,但是还是持续增长
试着讲清楚你的疑问。
先看absoluteString的属性声明(XCode7.3.1版本),readonly && copy,看到两个属性,应该有一种直觉,这个值是一个计算值(可参考Swift的属性声明)。
@property (readonly, copy) NSString *absoluteString;
再看文档
Discussion
This property’s value is calculated by resolving the receiver’s string against its base according to the algorithm given in RFC 1808.
可以确定,absoluteString是一个计算值。如果你看过《Effective Objective-C 2.0》,那么可以再进一步,这个值是autorelease的。
autorelease的值在循环中会大量消耗内存,一般会使用autoreleasepool来降低内存峰值。
可以看下面两个测试例子
低内存版:
while (1) {
NSString *a = [[NSString alloc] initWithFormat:@"asdfasdfasfasdf"];
}
高内存版:
while (1) {
NSString *a = [NSString stringWithFormat:@"asdfasdfasfasdf"];
}
针对你的问题,稍加改动
while (1) {
@autoreleasepool {
NSString *value = dict[url.absoluteString];
}
}
可以看到,内存已经不会暴涨了。