ABCmouse iOS内存泄露修复方案

什么是内存泄露?

  • 名词解释

内存泄漏(Memory leak)是在计算机科学中,由于疏忽或错误造成程序未能释放已经不再使用的内存。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费。

  • 后果

内存泄漏会因为减少可用内存的数量从而降低计算机的性能。最终,在最糟糕的情况下,过多的可用内存被分配掉导致全部或部分设备停止正常工作,或者应用程序崩溃。


如何定位内存泄露的具体位置?

在修复之前,首先要找到问题,针对内存泄露主要的分析工具有以下两个:

  • 第一种:静态分析方法(Analyze)
  • 第二种:动态分析方法(Instruments工具库中的Leaks)

接下来分别看看以上两种方法是如何执行的:

1. 运行Analyze

点击Product->Analyze

执行完成后,可以根据提示,修改相应位置:

可以看到,还是可以得到不少有用的信息,问题主要集中在:

  1. malloc之后没有free,如:
    ABCmouse iOS内存泄露修复方案_第1张图片
  2. new之后没有delete,如:
    ABCmouse iOS内存泄露修复方案_第2张图片
  3. 使用CoreFoundation的对象没有释放,如:
    ABCmouse iOS内存泄露修复方案_第3张图片
修复方法:

以上就按照提示进行修复就好,该释放的时候释放,还是比较傻瓜式的。

2. 运行Instruments的Leaks分析

  • CNCopyCurrentNetworkInfo

首先可以看到有非常多的CNCopyCurrentNetworkInfo的泄露:

此时debug会发现拿到的CNCopyCurrentNetworkInfo是nil。

/*!
 @function CNCopyCurrentNetworkInfo
 @discussion Returns the network information for the specified interface when the requesting application meets one of following 3 requirements -.
	1. application is using CoreLocation API and has user's authorization to access location.
	2. application has used NEHotspotConfiguration API to configure the current Wi-Fi network.
	3. application has active VPN configurations installed.

	- An application that is linked against iOS 12.0 SDK and above must have the "com.apple.developer.networking.wifi-info" entitlement.
	- An application will receive a pseudo network information if it is linked against an SDK before iOS 13.0, and if it fails to meet any of the
	  above 3 requirements.
	- An application will receive NULL if it is linked against iOS 13.0 SDK or above, and if it fails to meet any of the above 3 requirements..

	Network Information dictionary will contain the following keys, and values:
	
	@textblock
	Keys                      : Values
	=======================================
	kCNNetworkInfoKeySSIDData : CFDataRef
	kCNNetworkInfoKeySSID     : CFStringRef
	kCNNetworkInfoKeyBSSID    : CFStringRef
	@/textblock
	
Pseudo network information will contain "Wi-Fi" SSID and "00:00:00:00:00:00" BSSID. For China region, the SSID will be "WLAN". @param interfaceName Name of the interface you are interested in @result Network Information dictionary associated with the interface. Returns NULL if an error was encountered. You MUST release the returned value. */ CFDictionaryRef __nullable CNCopyCurrentNetworkInfo (CFStringRef interfaceName)

从CNCopyCurrentNetworkInfo的注释可以看到,应用满足下面下个条件函数才会返回有效的网卡信息:

获得了定位服务权限的应用
正处于启用状态的 VPN 应用
使用 NEHotspotConfiguration(通过应用配置的 Wi-Fi 网路)

An application will receive NULL if it is linked against iOS 13.0 SDK or above, and if it fails to meet any of the above 3 requirements。

可以肯定是iOS13 设备没有满足上面三个条件时会出现泄露,这其实是苹果的Bug,在iOS12下会返回假信息,再手动释放,就不会有问题,但iOS13返回NULL,就无从释放了。

修改方案:

我们的应用并没有满足以上任何一个条件。在以上三个条件,第一个最简单,就申请第一个定位权限就好。
但是用户可能会拒绝给权限,这样的话,在获取前,可以再加个判断,如果是iOS13以后,并且用户未给定位权限,则直接返回,不去拿CNCopyCurrentNetworkInfo。

  • 下载器

然后会看到下载器模块的泄露非常多,并且给出的堆栈非常具有迷惑性,又有new方法的,又有rootAllocWithZone的,还有setObject:ForKey:的:



查看相关代码,并在网上查了各种资料,都没有什么进展,直到我发现了这个,提示NSURLSession泄露:
ABCmouse iOS内存泄露修复方案_第4张图片
通过NSURLSession的头文件我们发现,NSURLSession对于它的 delegate属性是强引用。这就意味着当session存在时,其delegate就不会被释放。另外,由session发起请求的缓存相关对象也会被其强引用并一直保留在内存中。

修改方案:

所以为了避免内存泄漏,根据Apple文档,当一个session不再使用时,我们应该调用finishTasksAndInvalidate或者invalidateAndCancel把session显式地置为无效(invalidated),以释放对相关对象的引用。

修改后,跟下载器相关的泄露就都没有了


TODO

  1. cocos2d_libs工程下的MRC代码中内存泄露;
  2. ARMPlayer中的内存泄漏问题

上面两个问题,等解决后再补充。

你可能感兴趣的:(iOS,ios,内存优化)