iOS UIImage 找到性能的拖油瓶

1,505 阅读3分钟

上一片文章当中比较了UIImage 加载的几种方式性能(iOS UIImage 加载图片性能分析)。

从性能上来看,从Asset加载图片性能是imageNamed加载逻辑目录性能的9倍,是从逻辑目录imageWithContentsOfFile的2倍。其中使用imageNamed加载逻辑目录图片性能最差,对App的性能影响最大。

在实际开发的过程中,很多开发在使用习惯是可能会把图片放在逻辑目录里面,并且使用imageNamed去加载图片。他们并没有意思到这个方式对App应用性能影响。当代码量越来越大,这个问题累计之后,App性能会收到挑战。

项目背景

我在接受一个存量工程之后尝试在这方面做一些改进,我的目的是找到性能的拖油瓶,使用imageWithContentsOfFile加载图片的性能还能接受,最主要是找到从逻辑目录使用imageNamed的加载方法。

存在问题

代码里面已经存在了大量的图片,有些功能可能已经废弃了,但是图片还存在,另外就是难以从代码的角度去分析到底imageNamed到底是加载了Asset的图片或者逻辑目录的图片,工作量太大。

解决思路

下面是三种加载方式的函数栈:

1.imageNamed加载Asset资源


2.imageNamed加载逻辑目录资源

3.imageWithContentsOfFile加载逻辑目录资源


首先imageWithContentsOfFile加载图片方法与前两中方法函数栈完全不一样,我的重点是区分出imageNamed加载不同资源的差异。

从1与2图函数栈当中可以看到,两者在

[CUICatalog _resolvedRenditionKeyFromThemeRef:withBaseKey:scaleFactor:deviceIdiom:deviceSubtype:displayGamut:layoutDirection:sizeClassHorizontal:sizeClassVertical:memoryClass:graphicsClass:graphicsFallBackOrder:]

函数路径一致的,但是在下一个调用方法出现分歧。

1方法调用了

[CUIStructuredThemeStore canGetRenditionWithKey:]

2方法调用了

[CUIMutableStructuredThemeStore canGetRenditionWithKey:]

从Instrument上能看到两者耗时差异非常大。这个分支的差异给了我思路来对两者进行区分,如果我能够记录调用[CUIMutableStructuredThemeStore canGetRenditionWithKey:]的代码,我就可以区分出imageNamed到底是加载的哪里的图片。

使用断点来实现函数栈抓取

我在工程当中新建了[CUIMutableStructuredThemeStore canGetRenditionWithKey:]符号的断点,然后运行整个工程,通过查看函数栈来找到使用imageNamed错误加载方法的位置。但在实际操作过程中发现,[CUIMutableStructuredThemeStore canGetRenditionWithKey:]不仅仅在UImage的方法中会调用,一些其他的系统方法,比如UINavigation的初始化过程中也会调用。最严重的是,一次[UIImage imageNamed:]加载逻辑目录图片的操作过程中,会多次(以几十)调用[CUIMutableStructuredThemeStore canGetRenditionWithKey:],导致这个调试过程根本无法进行。

我的目的是在读取一次[UIImage imageNamed:]方法时打印一次函数栈就可以,为了达到这个目的,我对断点增加了条件判断,并且增加了

[CUICatalog _resolvedRenditionKeyFromThemeRef:withBaseKey:scaleFactor:deviceIdiom:deviceSubtype:displayGamut:layoutDirection:sizeClassHorizontal:sizeClassVertical:memoryClass:graphicsClass:graphicsFallBackOrder:]

变成一下流程:


经过测试,上述方法可以找到对应的imageNamed错误使用的代码函数栈,但是由于打开了断点功能,这个测试过程中App会比较卡,但是不影响使用。

下面的断点的截图,可以供大家参数,有疑问请留言。