V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
iOS 开发实用技术导航
NSHipster 中文版
http://nshipster.cn/
cocos2d 开源 2D 游戏引擎
http://www.cocos2d-iphone.org/
CocoaPods
http://cocoapods.org/
Google Analytics for Mobile 统计解决方案
http://code.google.com/mobile/analytics/
WWDC
https://developer.apple.com/wwdc/
Design Guides and Resources
https://developer.apple.com/design/
Transcripts of WWDC sessions
http://asciiwwdc.com
Cocoa with Love
http://cocoawithlove.com/
Cocoa Dev Central
http://cocoadevcentral.com/
NSHipster
http://nshipster.com/
Style Guides
Google Objective-C Style Guide
NYTimes Objective-C Style Guide
Useful Tools and Services
Charles Web Debugging Proxy
Smore
jox
V2EX  ›  iDev

有没有办法提高 iOS 下 gif 的性能啊?

  •  
  •   jox · 2014-12-04 22:08:59 +08:00 · 10688 次点击
    这是一个创建于 3621 天前的主题,其中的信息可能已经有所发展或是发生改变。
    UIImageView渲染gif的原理应该是使用CAKeyframeAnimation来配置layer的contents属性,我用layer也能实现gif的渲染,并且跟使用UIImageView的性能差不多,而且UIImageView的animationImages和CAKeyframeAnimation的values属性都是copy的,这样就不能使用放在缓存里的已经解码过的图片来渲染gif的frame,导致有的时候滚动到含有gif的cell会抽一下。

    额,有些新手朋友大概不知道,UIImage内部是使用Image I/O来解码图片的,但是这个解码如果不强制执行的话,只有在要渲染的时候才会进行解码,如果保留UIImage指针的话,之后使用这个UIImage来渲染图片就不需要解码了,只需要渲染,所以静态图片只要解码过一次可以很快地渲染,但是gif动画就不行了,因为在给UIImageView或者layer提供gif的时候不是retain是copy过去的,这样就用不了之前已经解码过的图片数据了,额,我感觉这个应该是有意这么做的,可能是怕开发者乱渲染gif导致内存太高进而导致应用被杀死。

    因为是copy的,提前强制解码也不好使了,我看百度贴吧的iOS应用是直接放弃gif了,网页版的gif到了手机上就全是静态的了。。。。我估摸着他们也遇到了同样的问题。

    有没有iPhone上的应用渲染gif这块儿做的不错的?有人知道吗?非常感谢
    19 条回复    2015-08-06 10:49:55 +08:00
    matrix67
        1
    matrix67  
       2014-12-04 22:55:51 +08:00 via Android
    因为好像是因为费电
    jox
        2
    jox  
    OP
       2014-12-04 23:00:54 +08:00
    @matrix67 额,对,gif会导致cpu和gpu不停地工作,确实会费电。。。。我在考虑要不要也学别人只渲染第一帧意思意思得了。。。
    vixvix
        3
    vixvix  
       2014-12-04 23:04:41 +08:00
    狗了下, Flipboard有篇文章和源码,也许对你有用:
    http://engineering.flipboard.com/2014/05/animated-gif/
    gluttony
        4
    gluttony  
       2014-12-04 23:22:36 +08:00 via iPhone
    我以前试过各种gif解码库,都没有把图片下载到本地后套个静态HTML模版用UIWebView展示高效和省资源(很多段子应用都用的这个方法)。不过那时Flipboard的库还没出,你可以和UIWebView对比下。
    jox
        5
    jox  
    OP
       2014-12-04 23:23:57 +08:00
    @vixvix 嘿,我也刚刚找到了这篇文章,我下载下来了源码看了一下,他们是使用的CADisplayLink,然后手动渲染layer的内容,这样就可以不必重复解码了,我还不太清楚CADisplayLink怎么用,这个应该是我需要的,不过同样非常感谢
    jox
        6
    jox  
    OP
       2014-12-04 23:27:48 +08:00
    @gluttony UIWebView不行,不可能每个tablecell里都加载个UIWebView,最开始我就用过web view,效果不满意,而且有一些问题绕不过去,其实只要修改每帧的尺寸即使一下载入好几十帧也不怎么占内存,但是想要丝般顺滑,必须要跳过重复解码的过程。flipboard的方法应该能够解决我的问题,我需要好好研究一下他们的源代码
    Smartype
        7
    Smartype  
       2014-12-05 17:07:32 +08:00
    @jox 其实没有你想得那么复杂的。就是把所有的帧都解出来,然后主动绘制。什么叫做主动绘制呢?就是像游戏一样,使用cadisplaylink让系统通知你绘制。

    话说滚动的时候os会停止uiimageview的绘制来保证滚动的流畅性吧?这是你说的抽么?
    jox
        8
    jox  
    OP
       2014-12-05 17:28:42 +08:00
    @Smartype flipboard的做法就是使用CADisplayLink的,他们的做法是在需要刷新的时候直接使用解压缩过的图片来设置layer的contents属性。所以可以提前强制解压,或者只在第一次渲染的时候解压缩,后续的渲染保证流畅。

    如果使用UIImageVIew来渲染的话,使用UIImage的-(UIImage *)animatedImageWithImages:(NSArray *)images duration:(NSTimeInterval)duration
    来创建gif,然后直接set UIImageVIew的image属性也可以渲染gif,我不清楚UIImageView是怎么实现的,但是这么做的话的确会导致卡顿,因为UIImageView的animationImages的属性是copy,所以我猜测内部实现是使用的CAKeyframeAnimation,CAKeyframeAnimation来实现gif的话就是要把每一帧都copy过去,Image I/O要想直接使用解压缩过的数据似乎得保留UIImage指针,这个应该很容易就能测试出来,copy的话就不是之前的指针了,于是就又解压缩了一次,这个是在主线程发生的,否则无法解释卡顿现象啊。
    Smartype
        9
    Smartype  
       2014-12-07 14:10:46 +08:00
    @jox 我的意思是拖动的时候应该是挂起了runloop中的timer。导致卡顿的不是复制或者解码这些操作。
    和uikit不同,cadisplaylink是用来做游戏的,是要在拖动的时候渲染的。这是我的理解。

    如果是你认为是资源不足的原因的话,可以用一个小得可怜的gif试试。看拖动的时候是不是不会渲染。
    jox
        10
    jox  
    OP
       2014-12-07 16:07:30 +08:00
    @Smartype 今天我拿Instruments测试了一下,证明我之前的猜测是错误的,你说的没错,导致卡顿的原因不是因为解码,我用一个超大的gif,然后使用同一个UIImage来创建UIImageView,然后加到当前view的hierarchy里,只有第一次是需要解码的,后面就都不用解码了,这是Time Profiler的截图:

    lldong
        11
    lldong  
       2014-12-08 15:51:59 +08:00
    像 Twitter 一样转成视频播放
    Smartype
        12
    Smartype  
       2014-12-08 20:25:49 +08:00 via iPhone
    @jox 所以现在问题很明了了。是因为uiImageview的动画是用cftimer驱动的,而这个timer所在的runloop在滚动的时候被被挂起了,解决的方法要么是处理滚动,续上timer,不让出现跳帧。要么就是cadisplaylink,滚还是不滚,我都要播放。但是滚动的时候我是不关心动画的,我的话,会让滚动完了接着放
    jox
        13
    jox  
    OP
       2014-12-08 20:56:30 +08:00
    @Smartype 我也打算在滚动结束之后再继续播放动画!非常感谢!

    不过只要gif已经显示了,uiimageview在滚动的时候动画会一直播放,并且滚动很流畅,现在卡顿只有在接下来的需要渲染的table row里有很多数据的时候才会发生
    Smartype
        14
    Smartype  
       2014-12-08 21:29:30 +08:00 via iPhone
    @jox 那你可以异步渲染 tableviewcell 嘛
    jox
        15
    jox  
    OP
       2014-12-08 22:01:11 +08:00
    @Smartype 渲染只能在主线程做吧?怎么异步渲染啊?
    Smartype
        16
    Smartype  
       2014-12-08 23:51:12 +08:00 via iPhone
    用operation queue或者dispatch asyn在绘制在一个image graphics context里,然后好了后直接把这个丢给main thread显示就好,应该可以做到丝般润滑
    jox
        17
    jox  
    OP
       2014-12-09 00:04:59 +08:00
    @Smartype 这个我知道的,我以为你的意思是GPU渲染都能异步进行呢。我现在就是这么做的,拿到数据后在后台把文字都画到image context上,画好后把image设为layer的contents作为背景,然后再把图片异步加进去。就是现在图片这块儿还没处理好,如果一个cell里有好几个gif的话会卡顿。
    jox
        18
    jox  
    OP
       2014-12-27 14:27:54 +08:00
    这个帖子是错误的,使用Image I/O创建的UIImage对象copy之后还是会使用解码过的图片数据,很尴尬,这里我搞错了一些东西,假设有这样的两个属性:

    @property (retain, nonatomic) UIImage *retainImage;
    @property (copy, nonatomic) UIImage *copyImage;

    使用Image I/O创建了一个开启缓存的图片,并分别赋值给上面两个属性

    UIImage *image = ...

    self.retainImage = image;
    self.copyImage = image;

    到这里一共存在三个类型为UIImage的指针,两个类型为UIImage的对象,一个是使用Image I/O创建的,一个是在赋值给copyImage属性的时候创建的,ARC会分别计算这两个UIImage对象的引用数来决定是否要释放这些对象所占用的内存,但是需要指出的是,这两个UIImage对象里的_imageRef指向的是同一个地址!Image I/O是根据这个地址来决定是否要使用解码过的图片的,那么我的这个帖子根本就是错误的,如果有人被我误导了,在这里我向你道歉。

    也许苹果开发swift的原因之一就是因为有些开发者会像我一样对跟指针相关的东西产生困惑吧,虽然苹果在让使用obj c的开发者尽量远离手动管理内存方面做了很多努力,但是只要使用指针就难免要和程序背后冰冷的机器打交道,哈,不过也不知道v2ex上有多少人能看出来我的这个帖子是错误的呢?如果有人看出来却没有说出来,我只能说兄弟你不厚道啊。
    ichanne
        19
    ichanne  
       2015-08-06 10:49:55 +08:00
    最近在做多个gif动态表情一起发的功能, 卡顿啊
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1400 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 43ms · UTC 17:30 · PVG 01:30 · LAX 10:30 · JFK 13:30
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.