最近项目中有截屏分享的需求,截取整个uitableview的contentview,然后分享。
截屏的思路是分段截取,网上搜了有人给的方法是将uitableview的frame设置成contentsize大小,铺开之后一次性render,但是这样可以想象如果tableview太长,内存肯定要崩,而且铺开tableview之后,cell的重用机制就失效了,想想要初始化多少个cell来展示所有数据。因此最好是分屏截取,截一张,保存在本地,然后截取滚动到下一块。依次全部截取完成之后,再把所有图片拼接起来。
但是调试过程中发现内存仍然会暴增,每截一次图,内存就会增加一节,并且不会回落。instrument调试发现所有内存的增加都是叫一个VM:CG raster data 的对象
网上各种查,大部分讲的都是sdwebimage的问题,的确,sdwebimage为了加快图片载入速度,默认开启了使用内存来缓存图片信息,如果发现在滚动tableview时内存暴增,可能是因为这个原因,关闭这个选项即可:
sd也给出了说明:
但是截图过程中,如果只是简单的从context中获取图片的代码,发现也会导致内存暴增,就要考虑其他原因了:
instrument表现内存增加如下:
会发现,多次截图,内存稳步递增,cgrasterdata对象生成的个数跟全部个数相等,意味着之前生成的全部没有释放掉。点击右侧箭头进入,会发现所有的对象产生的地方都是相同的:
双击进入代码,会发现对象初始化的地方在:
UIImage*image =UIGraphicsGetImageFromCurrentImageContext();
这一行,也就是说该对象实在生成image的时候产生的。未释放的原因就可能是dealloc出问题了
经过各种demo排查,发现是我在uiimage的分类中重写了dealloc方法。。。用来释放运行时动态添加的属性。删掉dealloc方法后正常。因此,在分类中千万要小心替换系统的方法,如果重写了要注意调回被你替换调的系统方法,因为分类与继承不同,继承可以调super去调父类的方法,分类的话就是完全覆盖了分类的方法。分类里方法的命名也尽量带上自己的前缀,因为不能保证某个版本里面就可能会出现新的方法与自己的方法同名
如果有时候一定要在分类中重写系统的方法(尤其是dealloc最经常,需要释放一些资源)可以参考下面的写法:
原理:load时通过运行时交换dealloc与customdealloc的实现,这样系统调用dealloc时会调用到我们自定义的实现里,而在自定义的实现里重新用performselector去调用customdealloc的方法,因为已经叫唤过,会调用到系统的dealloc的实现,从而实现在不覆盖系统方法的情况下添加自定义的操作
以上纯属个人猜测与实践,使用之后解决了uiimage无法释放的问题。至于有没有其问题还么发现,希望小伙伴们发现了的话及时留言告知,可以一起探索,谢谢~~
第一次写文章哎!ios菜鸟一枚,写的不好请见谅~~