分配,分配,分配
接下来的仪器是分配工具。它能给出你所有创建和存储它们的内存的详细信息,它也显示你保留了每个对象的计数。
关闭仪器,回到Xcode和选择Product->Profile。然后,从选择器分配并单击配置文件。如下图:
程序再次打开 然后你会看到
这个时候你会发现两个曲目。一个叫(分配)Allocations,以及一个被称为VM Tracker(虚拟机跟踪)。该分配轨道将详细在本教程中讨论;虚拟机跟踪也是非常有用的,但更复杂一点。
所以你的错误会追踪下?
有隐藏的项目,你可能不知道有东西在那儿。你可能已经听说了内存泄漏。但你可能不知道的是,其实有两种泄漏。
第一个是真正的内存泄漏,一个对象尚未被释放,但是不再被引用的了。因此,存储器不能被重新使用。
第二类泄漏是比较麻烦一些。这就是所谓的“无界内存增长”。这发生在内存继续分配,并永远不会有机会被释放。
如果永远这样下去你的程序占用的内存会无限大,当超过一定内存的话 会被系统的看门狗给kill掉.
建立一个场景,你可以检测出无限的内存增长。首先,在应用程序使10个不同的搜索(不要用已经存在的搜索)。确保搜索的一些结果!现在让程序等待几秒钟。
你应该已经注意到,在分配的轨道图不断上升。这是告诉你的,内存被分配了。它的这一特征,将引导你找到无限的内存增长。
你将要执行的是“heap shot analysis”。为此,按这个按钮叫“Mark Heap”。你会发现的详细面板左侧的按钮
按下它,你会看到一个红色的标志出现在轨道上,像这样:
heap shot分析的目的是执行一个动作多次,看看如果内存是否无限增长。搜索一个内容,稍等几秒加载图像,然后返回主页。然后再标记堆。反复这样做不同的搜索。
演戏几个搜索后,仪器会看起来像这样:
这时你应该会疑问。图中的蓝色是怎么回事了,你继续这样操作10次这样的搜索 蓝色还不断变高:
那肯定是不好的。别急,有什么关于内存的警告?你知道这些,对不对?内存警告是告诉一个应用程序,内存警告是ios处理app最好的方式尤其是在内存越来越吃紧的时候,你需要清除一些内存。
内存一直增长其实也不一定是你的代码除了问题,也有可能是UIKit 系统框架本身导致的.
通过选择HardwareSimulate内存警告在iOS模拟器的菜单栏模拟内存警告。你会发现,记忆体使用量出现小幅回落,但绝对不会回到它应该的。所以还是有无限的内存增长发生的地方。
究其原因,堆出手做钻进搜索的每次迭代后,你可以看到内存的分配每个镜头之间。一起来看看在详细信息面板,你会看到一堆一堆的镜头。
在iOS模拟器的菜单栏中选择hardwaresimulate内存警告模拟内存警告。你会发现内存使用会出现小幅回落,但肯定不会回到它应该在的地方。
每一次的搜索后做你可以看到内存已拍摄之间的分配。在详细信息面板看一看,你会看到一好多堆镜头。
稳准狠
第一个堆镜头作为参照,然后随便打开一个堆镜头,你会看到如下:
靠,这是一个很大的对象!从哪里开始看呢?
最好的方式是通过列表,你在你的应用程序直接使用的类。在这种情况下,HTTPHeaderDict,CGRegion,CGPath,CFNumber,等等都是可以忽略了。
但是,一个突出的是UIImage,这肯定是在你程序使用的。点击上的UIImage左侧的箭头显示的完整列表。选择一个,在扩展详细信息面板:
图中灰色的是系统库,黑色部分是你应用的代码,要获得此跟踪更多的上下文,双击唯一的黑框ImageCache方法,这时候将掉转到如下方法
- - (void)downloadImageAtURL:(NSURL*)url completionHandler:(ImageCacheDownloadCompletionHandler)completion {
- UIImage *cachedImage = [self imageForKey:[url absoluteString]];
- if (cachedImage) {
- completion(cachedImage);
- } else {
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- NSData *data = [NSData dataWithContentsOfURL:url];
- UIImage *image = [UIImage imageWithData:data];
- [self setImage:image forKey:[url absoluteString]];
- dispatch_async(dispatch_get_main_queue(), ^{
- completion(image);
- });
- });
- }
- }
工具是非常有用的,你现在要努力通过自己的代码思考发生了什么.看看通过上面的方法,你会看到它调用一个名为setImage方法:forKey:。这种方法在缓存以防它再次使用以后的应用程序的图像。啊!那么这肯定听起来像它可能是一个问题!
一起来看看该方法的实现:
- - (void)setImage:(UIImage*)image forKey:(NSString*)key {
- [_cache setObject:image forKey:key];
- }
从网络上下载一个图片添加字典中,你会注意到这些图片从来没有从字典清楚过。
,这就是内存为什么会一直增长,因为应用程序并不会从缓存里删除东西.它只会一直增加他们。
要解决此问题,你需要的是ImageCache收到UIApplication内存吃紧的警告时.清理缓存。
为了使ImageCache能接收通知,修改init方法如下:
- - (id)init {
- if ((self = [super init])) {
- _cache = [NSMutableDictionary new];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(memoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
- }
- return self;
- }
注册UIApplicationDidReceiveMemoryWarningNotification执行memoryWarning:方法。
- - (void)memoryWarning:(NSNotification*)note {
- [_cache removeAllObjects];
- }
memoryWarning删除缓存中的所有对象。这将确保没有持图像。
为了测试此修复程序,再次启动仪器(从Xcode中有cmd)和重复的步骤。不要忘了在模拟结束内存警告!
注意:请确保您从Xcode中退出,重新构建,而不是仅仅点击仪器仪表上的红色按钮,以确保您使用的是最新的代码。
这一次分析应该是这样的:
这个时候,内存受到内存警告后急剧下降。但还是有一些内存整体增长,但远不及像以前那样。
究其原因还是有一定的增长确实是由于系统库,并没有太多可以做的。看来,系统库不释放所有的内存,这可能是由设计或可能是一个错误。你可以在你的应用程序做的是释放尽可能多的内存越好,你已经做到这一点!
干得好!还有一个问题,修补了, - 仍然有泄漏,你还没有解决的第一种类型的问题。